# ====================
# DATA INTAKE
# Reading in raw CSV data
# ====================

loan_default <- read.csv("https://raw.githubusercontent.com/saltwatersoup/MyCV/refs/heads/main/Data%20Sets/Loan%20Default%20Data/BankLoanDefaultDataset.csv")

# Copying loan_default
loan_default_Mod01 <- loan_default

# List of starting variables
RawVars <- names(loan_default)
# ====================
# GENERATING CATEGORICAL VARIABLES FOR BINARY VARIABLES
# Generating Loan_Type
# Generating Default_YN
# loan_default_Mod01 is a copy of loan_default that has binary variables reflected as categorical variables
# ====================

# Creating variable Loan_Type which reflects Car_loan, Personal_loan, Home_loan, and Education_loan as one categorical variable.
loan_default_Mod01 <- loan_default_Mod01 %>% 
  mutate(
    Loan_Type = case_when(
      Car_loan == 1 ~ "Car",
      Personal_loan == 1 ~ "Personal",
      Home_loan == 1 ~ "Home",
      Education_loan == 1 ~ "Education",
      TRUE ~ "Other"
    )
  )

# Creating variable Default_YN which reflects Default as a categorical variable.
loan_default_Mod01 <- loan_default_Mod01 %>% 
  mutate(
    Default_YN = case_when(
      Default == 0 ~ "No Default",
      Default == 1 ~ "Defaulted",
      TRUE ~ NA
    )
  )
# ====================
# CAPITALIZING ALL CATEGORICAL VARIABLES
# By default, some of the character responses are lowercase
# loan_default_Mod02 is a copy of loan_default_Mod01 that has character entries with uppercase first letters
# ====================

# Copying loan_default_Mod01
loan_default_Mod02 <- loan_default_Mod01

# Selecting character variables and making their first letters uppercase
loan_default_Mod02[sapply(loan_default_Mod02, typeof) == "character"] <- 
  loan_default_Mod02[sapply(loan_default_Mod02, typeof) == "character"] %>% 
  sapply(
    function(x) {
      substr(x, 1, 1) <- toupper(substr(x, 1, 1))
      x
    }
  )
# ====================
# DATA MODIFICATION
# Adding missing values
# loan_default_Mod03 is a copy of loan_default_Mod02 with missing values
# loan_default_MissLoc shows the locations of all missing values via TRUE
# loan_default_True shows all observations from loan_default_Mod02 that were given missing values in loan_default_Mod03
# ====================

# Copying loan_default_Mod02
loan_default_Mod03 <- loan_default_Mod02

# Creating random observation IDs and replacing the corresponding observations with missing

# loan_default_Mod03$Checking_Amount[sample(1:1000, 100, replace = FALSE)] <- NA
# loan_default_Mod03$Term[sample(1:1000, 100, replace = FALSE)] <- NA
# loan_default_Mod03$Credit_score[sample(1:1000, 100, replace = FALSE)] <- NA
loan_default_Mod03$Gender[sample(1:1000, 68, replace = FALSE)] <- NA
loan_default_Mod03$Marital_status[sample(1:1000, 87, replace = FALSE)] <- NA
# loan_default_Mod03$Car_loan[sample(1:1000, 100, replace = FALSE)] <- NA
# loan_default_Mod03$Personal_loan[sample(1:1000, 100, replace = FALSE)] <- NA
# loan_default_Mod03$Home_loan[sample(1:1000, 100, replace = FALSE)] <- NA
# loan_default_Mod03$Education_loan[sample(1:1000, 100, replace = FALSE)] <- NA
loan_default_Mod03$Emp_status[sample(1:1000, 136, replace = FALSE)] <- NA
# loan_default_Mod03$Amount[sample(1:1000, 100, replace = FALSE)] <- NA
# loan_default_Mod03$Saving_amoun[sample(1:1000, 100, replace = FALSE)] <- NA
loan_default_Mod03$Emp_duration[sample(1:1000, 201, replace = FALSE)] <- NA
loan_default_Mod03$Age[sample(1:1000, 159, replace = FALSE)] <- NA
# loan_default_Mod03$No_of_credit_account[sample(1:1000, 100, replace = FALSE)] <- NA

# Showing the locations of all missing values via TRUE
loan_default_MissLoc <- sapply(loan_default_Mod03, is.na)

# Showing all rows of complete data that were given missing values
loan_default_True <- loan_default_Mod02[(rowSums(sapply(loan_default_Mod03, is.na)) > 0), ]
# ====================
# CATEGORIZING VARIABLES
# Categorizing variables in loan_default_Mod03 into binary, numeric, and categorical
# ====================

# Identifying variable types
loan_default_VarType <- 
  loan_default_Mod03 %>% 
  sapply(typeof)

# Identifying binary variables
# Variables that contain 0, 1, or NA
loan_default_VarType[
  apply(
    loan_default_Mod03, 
    2, 
    function(x){all(match(x, c(0, 1, NA), nomatch = FALSE))}
  )
] <- "Binary"

# Identifying numeric variables
# Variables that are numbers but not binary
loan_default_VarType[loan_default_VarType == "integer"] <- "Numeric"

# Identifying categorical variables
# Variables that are characters
loan_default_VarType[loan_default_VarType == "character"] <- "Categorical"

1 Introduction

With real-world data, it is common to encounter missing values resulting from entry error, non-response, hardware failure, etc. In some cases, ignoring missing values might not make a significant impact in a study’s conclusions, but in other cases, ignoring missing values can lead to significant deviations from the truth or even false conclusions.

This program serves as an exercise in addressing missing data by using BankLoanDefaultDataset.csv as its base. Because BankLoanDefaultDataset.csv has no missing values to start with, some values were replaced with NA to simulate an incomplete dataset.

The following plots show which variables contain missing values and how many. Hovering over each bar gives further details.

# ====================
# PLOTTING MISSING VALUES
# ====================

# Generating data frame of missing values per variable
MissDatCounts <- data.frame(
  Variables = names(loan_default_Mod03),
  VarType = loan_default_VarType,
  Missing = colSums(is.na(loan_default_Mod03))
)

# Generating interactive plot using plotly
Plot_MissingVals <- 
  # Taking a subset of MissDatCounts, so only entries with > 0 missing values will be displayed
  subset(MissDatCounts, Missing > 0) %>% 
  # Passing the subset to plot_ly
  plot_ly(
    x = ~Variables,
    y = ~Missing,
    split = ~VarType,
    hovertemplate = ~paste0(
      "<b>Count</b>: ", Missing, " of 1000<br>",
      "<b>\U0025 Missing</b>: ", round(Missing / 1000 * 100, digits = 3), "\U0025"
    )
  ) %>% 
  layout(
    title = list(
      text = "Missing Values per Variable"
    ),
    xaxis = list(
      title = "Variables with Missing Values",
      categoryorder = "trace"
    ),
    yaxis = list(
      title = "Number of Missing Values"
    ),
    legend = list(
      title = list(text = "<b> Variable Type </b>"),
      bgcolor = "#E2E2E2",
      bordercolor = "#FFFFFF",
      borderwidth = 2
    )
  )

# Outputting plot
Plot_MissingVals
# ====================
# PLOTTING CATEGORICAL VARIABLES WITH MISSING VALUES
# ====================

# Subsetting loan_default_Mod03 into the variables that are categorical and have missing data
CatVars_Miss <- 
  loan_default_Mod03[
    (loan_default_VarType == "Categorical") & (colSums(is.na(loan_default_Mod03)) > 0)
  ]

Fig_Data <- c()

for(i in 1:ncol(CatVars_Miss)) {
  Fig_Data[[i]] <- CatVars_Miss %>% 
    count(.data[[names(CatVars_Miss)[i]]])
}

# Preparing a list of subplots
NumFig <- c()
# Using a for loop to generate a subplot per variable in CatVars_Miss
for(i in 1:length(Fig_Data)){
  NumFig[[i]] <- plot_ly()
  for(j in 1:nrow(Fig_Data[[i]])) {
    NumFig[[i]] <- NumFig[[i]] %>% 
      add_trace(
        x = names(CatVars_Miss)[i],
        y = Fig_Data[[i]][j,2], 
        type = "bar",
        name = Fig_Data[[i]][j,1],
        legendgroup = names(CatVars_Miss)[i],
        legendgrouptitle = list(text = names(CatVars_Miss)[i]),
        hovertemplate = paste0(
          "<b>Count</b>: ", Fig_Data[[i]][j,2], " of 1000<br>",
          "<b>Percentage</b>: ", round(Fig_Data[[i]][j,2] / 1000 * 100, digits = 3), "\U0025"
        )
      ) %>% 
      layout(
        yaxis = list(range = c(0, 700))
      )
  }
}

Fig_CatVars_Miss <- 
  subplot(NumFig[[1]], NumFig[[2]], NumFig[[3]], nrows = 1, margin = 0.05) %>% 
  layout(
    title = "Categorical Variables with Missing Values",
    legend = list(
      title = list(text = "<b> Categorical <br> Responses </b>"),
      bgcolor = "#E2E2E2",
      bordercolor = "#FFFFFF",
      borderwidth = 2
    ),
    yaxis = list(
      title = "Number of Entries"
    )
  )

Fig_CatVars_Miss
# ====================
# PLOTTING NUMERIC VARIABLES WITH MISSING VALUES
# ====================

# Subsetting loan_default_Mod03 into the variables that are categorical and have missing data
NumVars_Miss <- 
  loan_default_Mod03[
    (loan_default_VarType == "Numeric") & (colSums(is.na(loan_default_Mod03)) > 0)
  ] 

NumVars_Miss_Mod01 <- NumVars_Miss %>% 
  sapply(is.na) %>% 
  ifelse(NA, "Non-missing") %>% 
  data.frame()

Fig_Data <- c()

for(i in 1:ncol(NumVars_Miss_Mod01)) {
  Fig_Data[[i]] <- NumVars_Miss_Mod01 %>% 
    count(.data[[names(NumVars_Miss_Mod01)[i]]])
}

# Preparing a list of subplots
NumFig <- c()
# Using a for loop to generate a subplot per variable in NumVars_Miss_Mod01
for(i in 1:length(Fig_Data)){
  NumFig[[i]] <- plot_ly()
  for(j in 1:nrow(Fig_Data[[i]])) {
    NumFig[[i]] <- NumFig[[i]] %>% 
      add_trace(
        x = names(NumVars_Miss_Mod01)[i],
        y = Fig_Data[[i]][j,2], 
        type = "bar",
        name = Fig_Data[[i]][j,1],
        legendgroup = names(NumVars_Miss_Mod01)[i],
        legendgrouptitle = list(text = names(NumVars_Miss_Mod01)[i]),
        hovertemplate = paste0(
          "<b>Count</b>: ", Fig_Data[[i]][j,2], " of 1000<br>",
          "<b>Percentage</b>: ", round(Fig_Data[[i]][j,2] / 1000 * 100, digits = 3), "\U0025"
        )
      ) %>% 
      layout(
        yaxis = list(range = c(0, 850))
      )
  }
}

Fig_NumVars_Miss <- 
  subplot(NumFig[[1]], NumFig[[2]], nrows = 1, margin = 0.05) %>% 
  layout(
    title = "Numerical Variables with Missing Values",
    legend = list(
      title = list(text = "<b> Responses </b>"),
      bgcolor = "#E2E2E2",
      bordercolor = "#FFFFFF",
      borderwidth = 2
    ),
    yaxis = list(
      title = "Number of Entries"
    )
  )

Fig_NumVars_Miss

1.1 Imputation

Imputation is the process of replacing missing data with substitute values. Ideally, we want to impute values as close as possible to the true value that is missing. To that end, we must try to predict what the missing value would have been.

We will explore the following methods of imputation:

  • k-Nearest Neighbors (k-NN) for categorical variables with missing values
  • Random regression imputation for numerical variables with missing values
  • Multiple Imputation by Chained Equations (MICE) for all missing values

1.2 Feature Engineering

Feature engineering is the process of selecting, transforming, and creating feature variables to improve the performance of predictive models.

For the purpose of feature selection, we use a wrapper method, which consists of using a predictive model to evaluate the performance of different combinations of features then selecting the highest performing set of features. The caret package enables us to use recursive feature elimination (RFE) to output the list of features that give us the best performance. The following output gives us the best set of feature variables to predict the label variable, Default.

# ====================
# FEATURE SELECTION
# Dr. Peng's code was used as a reference
# ====================

results <- rfe(
  loan_default[, -1], 
  loan_default$Default, 
  sizes = c(1:5), 
  rfeControl = rfeControl(functions = rfFuncs, method = "cv", number = 10)
  )

predictors(results)
 [1] "Age"              "Credit_score"     "Checking_amount"  "Saving_amount"   
 [5] "Education_loan"   "Term"             "Personal_loan"    "Home_loan"       
 [9] "Car_loan"         "Amount"           "Emp_status"       "Emp_duration"    
[13] "Marital_status"   "No_of_credit_acc" "Gender"          

As RFE has returned all of our original feature variables, we proceed without eliminating any features from further analysis.

Visualizing all numerical non-binary variables, we can see that:

  • The narrow numerical range and steep drops of No_of_credit_acc make it a prime candidate for binning.
  • The remaining numerical variables are candidates for standardization to better support future modeling.
# ====================
# PLOTTING ALL NUMERICAL NON-BINARY VARIABLES
# ====================

# Selecting only numeric variables
NumVars <- select(loan_default_Mod03, where(is.numeric))
# Selecting eliminating any binary variables
NumVars <- NumVars[!apply(NumVars, 2, function(x){all(match(x, c(0, 1, NA), nomatch = FALSE))})]

# Preparing a list of subplots
NumFig <- c()
# Using a for loop to generate a subplot per variable in NumVars
for(i in 1:length(names(NumVars))){
  NumFig[[i]] <- plot_ly(
    x = NumVars[[i]], 
    # y = "", 
    type = "histogram",
    name = colnames(NumVars)[i]
  )
}

# Generating a plot that contains 8 subplots (one for each variable in NumVars) across 4 rows
Plot_NumVars <- 
  subplot(NumFig[[1]], NumFig[[2]], NumFig[[3]], NumFig[[4]], NumFig[[5]], NumFig[[6]], NumFig[[7]], NumFig[[8]], nrows = 4, margin = 0.05) %>% 
  layout(
    title = "Distributions of All Numerical Non-binary Variables",
    legend = list(
      title = list(text = "<b> Variable </b>"),
      bgcolor = "#E2E2E2",
      bordercolor = "#FFFFFF",
      borderwidth = 2
    )
  )

# Outputting plot
Plot_NumVars

Binning No_of_credit_acc according to the sharp drops in the histogram results in the creation of new variable No_of_credit_acc_Bins with the following breakdown:

# ====================
# BINNING No_of_credit_acc
# ====================

# Transforming No_of_credit_acc into ordinal categorical variables
loan_default_Mod04 <- loan_default_Mod03 %>% 
  mutate(
    No_of_credit_acc_Bins = cut(
      No_of_credit_acc,
      breaks = c(1, 2, 5, 9),
      include.lowest = TRUE
    )
  )

table(loan_default_Mod04$No_of_credit_acc_Bins)

[1,2] (2,5] (5,9] 
  633   333    34 

Standardization of the remaining seven numerical variables results in the following distribution:

# ====================
# FEATURE STANDARDIZATION
# ====================

# Normalization function from Dr. Peng
standardize <- function(x) {
  return((x - mean(x, na.rm = TRUE)) / sd(x, na.rm = TRUE))
}

# Normalizing numeric variables
loan_default_Mod04$Checking_amount_Stand <- 
  standardize(loan_default_Mod04$Checking_amount)
loan_default_Mod04$Term_Stand <- 
  standardize(loan_default_Mod04$Term)
loan_default_Mod04$Credit_score_Stand <- 
  standardize(loan_default_Mod04$Credit_score)
loan_default_Mod04$Amount_Stand <- 
  standardize(loan_default_Mod04$Amount)
loan_default_Mod04$Saving_amount_Stand <- 
  standardize(loan_default_Mod04$Saving_amount)
loan_default_Mod04$Emp_duration_Stand <- 
  standardize(loan_default_Mod04$Emp_duration)
loan_default_Mod04$Age_Stand <- 
  standardize(loan_default_Mod04$Age)

# List of standardized variables
StandVars <- c("Checking_amount_Stand", "Term_Stand", "Credit_score_Stand", "Amount_Stand", "Saving_amount_Stand", "Emp_duration_Stand", "Age_Stand")

# Numeric variable data for de-standardization
DeStandVarData <- 
  data.frame(
    Variable = c("Checking_amount", "Term", "Credit_score", "Amount", "Saving_amount", "Emp_duration", "Age"),
    Mean = c(
      mean(loan_default_Mod04$Checking_amount, na.rm = TRUE),
      mean(loan_default_Mod04$Term, na.rm = TRUE),
      mean(loan_default_Mod04$Credit_score, na.rm = TRUE),
      mean(loan_default_Mod04$Amount, na.rm = TRUE),
      mean(loan_default_Mod04$Saving_amount, na.rm = TRUE),
      mean(loan_default_Mod04$Emp_duration, na.rm = TRUE),
      mean(loan_default_Mod04$Age, na.rm = TRUE)
    ),
    SD = c(
      sd(loan_default_Mod04$Checking_amount, na.rm = TRUE),
      sd(loan_default_Mod04$Term, na.rm = TRUE),
      sd(loan_default_Mod04$Credit_score, na.rm = TRUE),
      sd(loan_default_Mod04$Amount, na.rm = TRUE),
      sd(loan_default_Mod04$Saving_amount, na.rm = TRUE),
      sd(loan_default_Mod04$Emp_duration, na.rm = TRUE),
      sd(loan_default_Mod04$Age, na.rm = TRUE)
    )
  )
# ====================
# PLOTTING ALL STANDARDIZED NUMERICAL VARIABLES
# ====================

# Selecting only standardized variables
StandNumVars <- loan_default_Mod04[StandVars]

# Preparing a list of subplots
NumFig <- c()
# Using a for loop to generate a subplot per variable in StandNumVars
for(i in 1:length(names(StandNumVars))){
  NumFig[[i]] <- plot_ly(
    x = StandNumVars[[i]], 
    # y = "", 
    type = "histogram",
    name = colnames(StandNumVars)[i]
  )
}

# Generating a plot that contains 8 subplots (one for each variable in StandNumVars) across 4 rows
Plot_StandNumVars <- 
  subplot(NumFig[[1]], NumFig[[2]], NumFig[[3]], NumFig[[4]], NumFig[[5]], NumFig[[6]], NumFig[[7]], nrows = 4, margin = 0.05) %>% 
  layout(
    title = "Distributions of All Standardized Numerical Variables",
    legend = list(
      title = list(text = "<b> Variable </b>"),
      bgcolor = "#E2E2E2",
      bordercolor = "#FFFFFF",
      borderwidth = 2
    )
  )

# Outputting plot
Plot_StandNumVars

2 Replacement Imputation for Categorical Features

The data contains missing values in the variables Gender, Marital_status, and Emp_status, and these missing values must be addressed before we can proceed with modeling. To that end, we use k-NN to impute the missing values. The results of this imputation can be seen in the following plot:

# ====================
# IMPUTING CATEGORICAL VALUES WITH kNN
# CatImpute is loan_default_Mod04 with imputed categorical values
# ====================

CatImpute <- kNN(
  loan_default_Mod04[RawVars], 
  variable = c("Gender", "Marital_status", "Emp_status"),
  k = 5
)
# ====================
# PLOTTING CATEGORICAL VARIABLES WITH IMPUTED VALUES
# ====================

# Subsetting loan_default_Mod04 into the variables that are categorical and have missing data
CatVars_Imp <- CatImpute[
  c("Gender", "Marital_status", "Emp_status", 
    "Gender_imp", "Marital_status_imp", "Emp_status_imp")
]

CatVars_Imp <- CatVars_Imp %>% 
  mutate(
    Gender = case_when(
      Gender_imp == TRUE ~ paste0(Gender, " (Imputed)"),
      TRUE ~ Gender
    ),
    Marital_status = case_when(
      Marital_status_imp == TRUE ~ paste0(Marital_status, " (Imputed)"),
      TRUE ~ Marital_status
    ),
    Emp_status = case_when(
      Emp_status_imp == TRUE ~ paste0(Emp_status, " (Imputed)"),
      TRUE ~ Emp_status
    ),
  )

Fig_Data <- c()

for(i in 1:ncol(CatVars_Imp)) {
  Fig_Data[[i]] <- CatVars_Imp %>% 
    count(.data[[names(CatVars_Imp)[i]]])
}

# Preparing a list of subplots
NumFig <- c()
# Using a for loop to generate a subplot per variable in CatVars_Imp
for(i in 1:3){
  NumFig[[i]] <- plot_ly()
  for(j in 1:nrow(Fig_Data[[i]])) {
    NumFig[[i]] <- NumFig[[i]] %>% 
      add_trace(
        x = names(CatVars_Imp)[i],
        y = Fig_Data[[i]][j,2], 
        type = "bar",
        name = Fig_Data[[i]][j,1],
        legendgroup = names(CatVars_Imp)[i],
        legendgrouptitle = list(text = names(CatVars_Imp)[i]),
        hovertemplate = paste0(
          "<b>Count</b>: ", Fig_Data[[i]][j,2], " of 1000<br>",
          "<b>Percentage</b>: ", round(Fig_Data[[i]][j,2] / 1000 * 100, digits = 3), "\U0025"
        )
      )
  }
}

Fig_CatVars_Imp <- 
  subplot(NumFig[[1]], NumFig[[2]], NumFig[[3]], nrows = 1, margin = 0.05, shareY = TRUE) %>% 
  layout(
    title = "Categorical Variables with Missing Values",
    legend = list(
      title = list(text = "<b> Categorical <br> Responses </b>"),
      bgcolor = "#E2E2E2",
      bordercolor = "#FFFFFF",
      borderwidth = 2
    ),
    yaxis = list(
      title = "Number of Entries"
    )
  )

Fig_CatVars_Imp

The results of this method of imputation roughly appear to reflect the distribution of the non-missing data. This would make sense if the data is missing at random.

3 Random Regression-based Imputation for Numerical Features

The data contains missing values in the variables Emp_duration and Age, and these missing values must be addressed before we can proceed with modeling. To that end, we use random regression to impute the missing values.

We selected the variable to reference by looking at the correlation plot between numerical variables. By having the highest correlations, No_of_credit_acc was chosen for Emp_duration and Saving_amount was chosen for Age.

# ====================
# PLOTTING CORRELATION BETWEEN NUMERICAL VARIABLES
# ====================

corrplot(
  cor(
    loan_default_Mod04[
      c(
        # "Default",
        "Checking_amount",
        "Term",
        "Credit_score",
        # "Gender",
        # "Marital_status",
        # "Car_loan",
        # "Personal_loan",
        # "Home_loan",
        # "Education_loan",
        # "Emp_status",
        "Amount",
        "Saving_amount",
        "Emp_duration",
        "Age",
        "No_of_credit_acc"
      )
    ],
    use = "complete.obs"
  ),
  type = "lower",
  tl.srt = 35,
  addCoef.col = 'grey50'
)

The following is the result of the imputation:

# ====================
# IMPUTING NUMERICAL VALUES WITH LINEAR REGRESSION
# NumImpute is loan_default_Mod04 with imputed numerical values
# Dr. Peng's code was used as a reference
# ====================

# Generating linear imputation model for Emp_duration
NumImpute_Model_Emp_duration <- lm(
  Emp_duration ~ 
    # Default + 
    # Checking_amount + 
    # Term + 
    # Credit_score + 
    # # Gender + 
    # # Marital_status + 
    # Car_loan + 
    # Personal_loan + 
    # Home_loan + 
    # Education_loan + 
    # # Emp_status + 
    # Amount + 
    # Saving_amount + 
    # Emp_duration +
    # Age + 
    No_of_credit_acc,
  data = loan_default_Mod04
)

# Generating linear imputation model for Age
NumImpute_Model_Age <- lm(
  Age ~ 
    # Default + 
    # Checking_amount + 
    # Term + 
    # Credit_score +
    # Gender + 
    # Marital_status + 
    # Car_loan + 
    # Personal_loan + 
    # Home_loan + 
    # Education_loan + 
    # Emp_status + 
    # Amount + 
    Saving_amount,
    # Emp_duration +
    # Age + 
    # No_of_credit_acc,
  data = loan_default_Mod04
)

# Copying loan_default_Mod04
NumImpute <- loan_default_Mod04

# Imputing Emp_duration with random regression
NumImpute$Emp_duration[is.na(loan_default_Mod04$Emp_duration)] <- 
  # Predicted value
  predict(
    NumImpute_Model_Emp_duration, 
    loan_default_Mod04[is.na(loan_default_Mod04$Emp_duration),], 
    type = "response"
  ) +
  # Added randomness via residual
  sample(
    resid(NumImpute_Model_Emp_duration), 
    sum(is.na(loan_default_Mod04$Emp_duration)), 
    replace = TRUE
  )

# Imputing Age with random regression
NumImpute$Age[is.na(loan_default_Mod04$Age)] <- 
  # Predicted value
  predict(
    NumImpute_Model_Age, 
    loan_default_Mod04[is.na(loan_default_Mod04$Age),], 
    type = "response"
  ) +
  # Added randomness via residual
  sample(
    resid(NumImpute_Model_Age), 
    sum(is.na(loan_default_Mod04$Age)), 
    replace = TRUE
  )
# ====================
# PLOTTING Emp_duration AND Age WITH IMPUTED VALUES
# ====================

Plot_NumImp_Parts <- NULL

# Generating subplot
Plot_NumImp_Parts[[1]] <- 
  plot_ly() %>% 
  add_trace(
    data = loan_default_Mod04,
    x = ~Emp_duration,
    name = "Non-missing",
    legendgroup = "Emp_duration",
    legendgrouptitle = list(text = "Emp_duration")
  ) %>% 
  add_trace(
    x = NumImpute$Emp_duration[is.na(loan_default_Mod04$Emp_duration)],
    name = "Imputed: Emp_Duration",
    legendgroup = "Emp_duration",
    legendgrouptitle = list(text = "Emp_duration")
  )

# Generating subplot
Plot_NumImp_Parts[[2]] <- 
  plot_ly() %>% 
  add_trace(
    data = loan_default_Mod04,
    x = ~Age,
    name = "Non-missing",
    legendgroup = "Age",
    legendgrouptitle = list(text = "Age")
  ) %>% 
  add_trace(
    x = NumImpute$Age[is.na(loan_default_Mod04$Age)],
    name = "Imputed: Age",
    legendgroup = "Age",
    legendgrouptitle = list(text = "Age")
  )

# Generating full plot
Plot_NumImp <- 
  subplot(Plot_NumImp_Parts[[1]], Plot_NumImp_Parts[[2]], nrows = 1, margin = 0.05, shareY = TRUE) %>% 
  layout(
    title = "Employment Duration and Age with Imputation",
    barmode = "stack",
    bargap = 0.1,
    legend = list(
      title = list(text = "<b> Responses </b>"),
      bgcolor = "#E2E2E2",
      bordercolor = "#FFFFFF",
      borderwidth = 2
    )
  )

# Outputting plot
Plot_NumImp

It is worth noting that this method has the possibility of imputing values outside of the possible range of the data. Therefore, it is worth considering an alternative method of imputation.

4 Multiple Imputation by Chained Equations (MICE)

Multiple Imputation by Chained Equations (MICE) is a method of imputing that iterates through each variable containing missing data and refining estimates. Therefore, this method allows us to impute Gender, Marital_status, Emp_status, Emp_duration, and Age at the same time.

The results of MICE can be seen in the plots below:

# ====================
# PERFORMING MULTIPLE IMPUTATION
# ====================

loan_default_MultImp <- 
  complete(
    mice(
      loan_default_Mod04[RawVars],
      method = c(
        # Default 
        NA,
        # Checking_amount 
        NA,
        # Term 
        NA,
        # Credit_score 
        NA,
        # Gender 
        "logreg",
        # Marital_status 
        "logreg",
        # Car_loan 
        NA,
        # Personal_loan 
        NA,
        # Home_loan 
        NA,
        # Education_loan 
        NA,
        # Emp_status 
        "logreg",
        # Amount 
        NA,
        # Saving_amount 
        NA,
        # Emp_duration
        "pmm",
        # Age 
        "pmm",
        # No_of_credit_acc
        NA
      ),
      maxit = 5,
      print = F,
      seed = 123
    )
  )
# ====================
# PLOTTING CATEGORICAL VARIABLES WITH MICE
# ====================


MICE_CatImp_Gender_Combo <- data.frame(
  Cat = c("Female", "Female (Imputed)", "Male", "Male (Imputed)", "NA (Imputed)"),
  Val = c(
    table(loan_default_Mod04$Gender)["Female"],
    table(loan_default_MultImp$Gender[is.na(loan_default_Mod04$Gender)], useNA = "always")["Female"],
    table(loan_default_Mod04$Gender)["Male"],
    table(loan_default_MultImp$Gender[is.na(loan_default_Mod04$Gender)], useNA = "always")["Male"],
    table(loan_default_MultImp$Gender[is.na(loan_default_Mod04$Gender)], useNA = "always")[3]
  )
)

MICE_CatImp_Gender_Combo$Cat <- MICE_CatImp_Gender_Combo$Cat %>% 
  factor(levels = MICE_CatImp_Gender_Combo[["Cat"]])


MICE_CatImp_Marital_status_Combo <- data.frame(
  Cat = c("Married", "Married (Imputed)", "Single", "Single (Imputed)", "NA (Imputed)"),
  Val = c(
    table(loan_default_Mod04$Marital_status)["Married"],
    table(loan_default_MultImp$Marital_status[is.na(loan_default_Mod04$Marital_status)], useNA = "always")["Married"],
    table(loan_default_Mod04$Marital_status)["Single"],
    table(loan_default_MultImp$Marital_status[is.na(loan_default_Mod04$Marital_status)], useNA = "always")["Single"],
    table(loan_default_MultImp$Marital_status[is.na(loan_default_Mod04$Marital_status)], useNA = "always")[3]
  )
)

MICE_CatImp_Marital_status_Combo$Cat <- MICE_CatImp_Marital_status_Combo$Cat %>% 
  factor(levels = MICE_CatImp_Marital_status_Combo[["Cat"]])


MICE_CatImp_Emp_status_Combo <- data.frame(
  Cat = c("Employed", "Employed (Imputed)", "Unemployed", "Unemployed (Imputed)", "NA (Imputed)"),
  Val = c(
    table(loan_default_Mod04$Emp_status)["Employed"],
    table(loan_default_MultImp$Emp_status[is.na(loan_default_Mod04$Emp_status)], useNA = "always")["Employed"],
    table(loan_default_Mod04$Emp_status)["Unemployed"],
    table(loan_default_MultImp$Emp_status[is.na(loan_default_Mod04$Emp_status)], useNA = "always")["Unemployed"],
    table(loan_default_MultImp$Emp_status[is.na(loan_default_Mod04$Emp_status)], useNA = "always")[3]
  )
)

MICE_CatImp_Emp_status_Combo$Cat <- MICE_CatImp_Emp_status_Combo$Cat %>% 
  factor(levels = MICE_CatImp_Emp_status_Combo[["Cat"]])


# ========================

Plot_MultImp_Cat_Parts <- NULL

# Generating subplot
Plot_MultImp_Cat_Parts[[1]] <- 
  plot_ly() %>% 
  add_trace(
    data = MICE_CatImp_Gender_Combo,
    x = "Gender",
    y = ~Val,
    name = ~Cat,
    type = "bar",
    legendgroup = "Gender",
    legendgrouptitle = list(text = "Gender")
  ) %>% 
  layout(barmode = "group")

# Generating subplot
Plot_MultImp_Cat_Parts[[2]] <- 
  plot_ly() %>% 
  add_trace(
    data = MICE_CatImp_Marital_status_Combo,
    x = "Marital_status",
    y = ~Val,
    name = ~Cat,
    type = "bar",
    legendgroup = "Marital_status",
    legendgrouptitle = list(text = "Marital_status")
  ) %>% 
  layout(barmode = "group")

# Generating subplot
Plot_MultImp_Cat_Parts[[3]] <- 
  plot_ly() %>% 
  add_trace(
    data = MICE_CatImp_Emp_status_Combo,
    x = "Emp_status",
    y = ~Val,
    name = ~Cat,
    type = "bar",
    legendgroup = "Emp_status",
    legendgrouptitle = list(text = "Emp_status")
  ) %>% 
  layout(barmode = "group")


# Generating full plot
Plot_MultImp_Cat <- 
  subplot(Plot_MultImp_Cat_Parts[[1]], Plot_MultImp_Cat_Parts[[2]], Plot_MultImp_Cat_Parts[[3]], nrows = 1, margin = 0.05, shareY = TRUE) %>% 
  layout(
    title = "Categorical MICE Results",
    bargap = 0.1,
    yaxis = list(
      title = ""
    ),
    legend = list(
      title = list(text = "<b> Responses </b>"),
      bgcolor = "#E2E2E2",
      bordercolor = "#FFFFFF",
      borderwidth = 2
    )
  )

# Outputting plot
Plot_MultImp_Cat

For the categorical variables, MICE appears to have resulted in similar imputation as with k-NN but has returned missing values. Therefore, MICE imputation may not be the optimal way to impute categorical variables.

# ====================
# PLOTTING NUMERICAL VARIABLES WITH MICE
# ====================


Plot_MultImp_Num_Parts <- NULL

# Generating subplot
Plot_MultImp_Num_Parts[[1]] <- 
  plot_ly() %>% 
  add_trace(
    data = loan_default_Mod04,
    x = ~Emp_duration,
    name = "Non-missing",
    legendgroup = "Emp_duration",
    legendgrouptitle = list(text = "Emp_duration")
  ) %>% 
  add_trace(
    x = loan_default_MultImp$Emp_duration[is.na(loan_default_Mod04$Emp_duration)],
    name = "Imputed: Emp_Duration",
    legendgroup = "Emp_duration",
    legendgrouptitle = list(text = "Emp_duration")
  ) %>% 
  layout(barmode = "stack")

# Generating subplot
Plot_MultImp_Num_Parts[[2]] <- 
  plot_ly() %>% 
  add_trace(
    data = loan_default_Mod04,
    x = ~Age,
    name = "Non-missing",
    legendgroup = "Age",
    legendgrouptitle = list(text = "Age")
  ) %>% 
  add_trace(
    x = loan_default_MultImp$Age[is.na(loan_default_Mod04$Age)],
    name = "Imputed: Age",
    legendgroup = "Age",
    legendgrouptitle = list(text = "Age")
  ) %>% 
  layout(barmode = "stack")

# Generating full plot
Plot_MultImp_Num <- 
  subplot(Plot_MultImp_Num_Parts[[1]], Plot_MultImp_Num_Parts[[2]], nrows = 1, margin = 0.05, shareY = TRUE) %>% 
  layout(
    title = "Numerical MICE Results",
    bargap = 0.1,
    legend = list(
      title = list(text = "<b> Responses </b>"),
      bgcolor = "#E2E2E2",
      bordercolor = "#FFFFFF",
      borderwidth = 2
    )
  )

# Outputting plot
Plot_MultImp_Num

For the numerical variables, MICE appears to have resulted in similar imputation as with random regression but does not generate values outside of the possible range. This improvement makes MICE preferable to random regression imputation.

LS0tDQp0aXRsZTogIkxvYW4gRGVmYXVsdCBEYXRhIFtJbXB1dGF0aW9uIGFuZCBGZWF0dXJlIEVuZ2luZWVyaW5nXSINCmF1dGhvcjogIktvamkgU2hpbW9tdXJhIg0KZGF0ZTogIiAiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6IA0KICAgIHRvYzogeWVzDQogICAgdG9jX2RlcHRoOiA0DQogICAgdG9jX2Zsb2F0OiB5ZXMNCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcw0KICAgIHRvY19jb2xsYXBzZWQ6IHllcw0KICAgIGNvZGVfZm9sZGluZzogaGlkZQ0KICAgIGNvZGVfZG93bmxvYWQ6IHllcw0KICAgIHNtb290aF9zY3JvbGw6IHllcw0KICAgIHRoZW1lOiBsdW1lbg0KICB3b3JkX2RvY3VtZW50OiANCiAgICB0b2M6IHllcw0KICAgIHRvY19kZXB0aDogNA0KICAgIGZpZ19jYXB0aW9uOiB5ZXMNCiAgICBrZWVwX21kOiB5ZXMNCiAgcGRmX2RvY3VtZW50OiANCiAgICB0b2M6IHllcw0KICAgIHRvY19kZXB0aDogNA0KICAgIGZpZ19jYXB0aW9uOiB5ZXMNCiAgICBudW1iZXJfc2VjdGlvbnM6IG5vDQogICAgZmlnX3dpZHRoOiAzDQogICAgZmlnX2hlaWdodDogMw0KZWRpdG9yX29wdGlvbnM6IA0KICBjaHVua19vdXRwdXRfdHlwZTogaW5saW5lDQotLS0NCg0KYGBgez1odG1sfQ0KDQo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPg0KDQovKiBDYXNjYWRpbmcgU3R5bGUgU2hlZXRzIChDU1MpIGlzIGEgc3R5bGVzaGVldCBsYW5ndWFnZSB1c2VkIHRvIGRlc2NyaWJlIHRoZSBwcmVzZW50YXRpb24gb2YgYSBkb2N1bWVudCB3cml0dGVuIGluIEhUTUwgb3IgWE1MLiBpdCBpcyBhIHNpbXBsZSBtZWNoYW5pc20gZm9yIGFkZGluZyBzdHlsZSAoZS5nLiwgZm9udHMsIGNvbG9ycywgc3BhY2luZykgdG8gV2ViIGRvY3VtZW50cy4gKi8NCg0KaDEudGl0bGUgeyAgLyogVGl0bGUgLSBmb250IHNwZWNpZmljYXRpb25zIG9mIHRoZSByZXBvcnQgdGl0bGUgKi8NCiAgZm9udC1zaXplOiAyMnB4Ow0KICBmb250LXdlaWdodDogYm9sZDsNCiAgY29sb3I6IERhcmtSZWQ7DQogIHRleHQtYWxpZ246IGNlbnRlcjsNCiAgZm9udC1mYW1pbHk6ICJHaWxsIFNhbnMiLCBzYW5zLXNlcmlmOw0KfQ0KaDQuYXV0aG9yIHsgLyogSGVhZGVyIDQgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciBhdXRob3JzICAqLw0KICBmb250LXNpemU6IDE4cHg7DQogIGZvbnQtd2VpZ2h0OiBib2xkOw0KICBmb250LWZhbWlseTogc3lzdGVtLXVpOw0KICBjb2xvcjogbmF2eTsNCiAgdGV4dC1hbGlnbjogY2VudGVyOw0KfQ0KaDQuZGF0ZSB7IC8qIEhlYWRlciA0IC0gZm9udCBzcGVjaWZpY2F0aW9ucyBmb3IgdGhlIGRhdGUgICovDQogIGZvbnQtc2l6ZTogMThweDsNCiAgZm9udC1mYW1pbHk6IHN5c3RlbS11aTsNCiAgY29sb3I6IERhcmtCbHVlOw0KICB0ZXh0LWFsaWduOiBjZW50ZXI7DQogIGZvbnQtd2VpZ2h0OiBib2xkOw0KfQ0KaDEgeyAvKiBIZWFkZXIgMSAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgZm9yIGxldmVsIDEgc2VjdGlvbiB0aXRsZSAgKi8NCiAgICBmb250LXNpemU6IDIycHg7DQogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogICAgY29sb3I6IG5hdnk7DQogICAgdGV4dC1hbGlnbjogY2VudGVyOw0KICAgIGZvbnQtd2VpZ2h0OiBib2xkOw0KfQ0KaDIgeyAvKiBIZWFkZXIgMiAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgZm9yIGxldmVsIDIgc2VjdGlvbiB0aXRsZSAqLw0KICAgIGZvbnQtc2l6ZTogMjBweDsNCiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgICBjb2xvcjogbmF2eTsNCiAgICB0ZXh0LWFsaWduOiBsZWZ0Ow0KICAgIGZvbnQtd2VpZ2h0OiBib2xkOw0KfQ0KDQpoMyB7IC8qIEhlYWRlciAzIC0gZm9udCBzcGVjaWZpY2F0aW9ucyBvZiBsZXZlbCAzIHNlY3Rpb24gdGl0bGUgICovDQogICAgZm9udC1zaXplOiAxOHB4Ow0KICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOw0KICAgIGNvbG9yOiBuYXZ5Ow0KICAgIHRleHQtYWxpZ246IGxlZnQ7DQp9DQoNCmg0IHsgLyogSGVhZGVyIDQgLSBmb250IHNwZWNpZmljYXRpb25zIG9mIGxldmVsIDQgc2VjdGlvbiB0aXRsZSAgKi8NCiAgICBmb250LXNpemU6IDE4cHg7DQogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogICAgY29sb3I6IGRhcmtyZWQ7DQogICAgdGV4dC1hbGlnbjogbGVmdDsNCn0NCg0KYm9keSB7IGJhY2tncm91bmQtY29sb3I6d2hpdGU7IH0NCg0KLmhpZ2hsaWdodG1lIHsgYmFja2dyb3VuZC1jb2xvcjp5ZWxsb3c7IH0NCg0KcCB7IGJhY2tncm91bmQtY29sb3I6d2hpdGU7IH0NCg0KPC9zdHlsZT4NCmBgYA0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCiMgPT09PT09PT09PT09PT09PT09PT0NCiMgRklMRSBTRVRVUA0KIyBDaGVja2luZyBhbmQgaW5zdGFsbGluZyBuZWNlc3NhcnkgbGlicmFyaWVzDQojIFNwZWNpZnlpbmcgd2hldGhlciB0aGUgUiBjb2RlLCB3YXJuaW5ncywgYW5kIG91dHB1dCB3aWxsIGJlIGluY2x1ZGVkIGluIHRoZSBvdXRwdXQgZmlsZXMNCiMgPT09PT09PT09PT09PT09PT09PT0NCg0KIyBDb25kaXRpb25hbGx5IGluc3RhbGxpbmcgcGFja2FnZXMNCmlmICghcmVxdWlyZSgia25pdHIiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygia25pdHIiKQ0KICAgbGlicmFyeShrbml0cikNCn0NCmlmICghcmVxdWlyZSgidGlkeXZlcnNlIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoInRpZHl2ZXJzZSIpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCn0NCmlmICghcmVxdWlyZSgicGFsbWVycGVuZ3VpbnMiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygicGFsbWVycGVuZ3VpbnMiKQ0KbGlicmFyeShwYWxtZXJwZW5ndWlucykNCn0NCmlmICghcmVxdWlyZSgicGxvdGx5IikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoInBsb3RseSIpDQpsaWJyYXJ5KHBsb3RseSkNCn0NCmlmICghcmVxdWlyZSgiR0dhbGx5IikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoIkdHYWxseSIpDQpsaWJyYXJ5KEdHYWxseSkNCn0NCmlmICghcmVxdWlyZSgibmFuaWFyIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoIm5hbmlhciIpDQpsaWJyYXJ5KG5hbmlhcikNCn0NCmlmICghcmVxdWlyZSgicG9vbCIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJwb29sIikNCmxpYnJhcnkocG9vbCkNCn0NCmlmICghcmVxdWlyZSgiREJJIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoIkRCSSIpDQpsaWJyYXJ5KERCSSkNCn0NCmlmICghcmVxdWlyZSgiUk15U1FMIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoIlJNeVNRTCIpDQpsaWJyYXJ5KFJNeVNRTCkNCn0NCmlmICghcmVxdWlyZSgicmFuZG9tRm9yZXN0IikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoInJhbmRvbUZvcmVzdCIpDQpsaWJyYXJ5KHJhbmRvbUZvcmVzdCkNCn0NCmlmICghcmVxdWlyZSgiZ2dpcmFwaCIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJnZ2lyYXBoIikNCmxpYnJhcnkoZ2dpcmFwaCkNCn0NCmlmICghcmVxdWlyZSgiaGlnaGNoYXJ0ZXIiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygiaGlnaGNoYXJ0ZXIiKQ0KbGlicmFyeShoaWdoY2hhcnRlcikNCn0NCmlmICghcmVxdWlyZSgiYnJvb20iKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygiYnJvb20iKQ0KbGlicmFyeShicm9vbSkNCn0NCg0KaWYgKCFyZXF1aXJlKCJWSU0iKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygiVklNIikNCmxpYnJhcnkoVklNKQ0KfQ0KaWYgKCFyZXF1aXJlKCJjb3JycGxvdCIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJjb3JycGxvdCIpDQpsaWJyYXJ5KGNvcnJwbG90KQ0KfQ0KaWYgKCFyZXF1aXJlKCJtaWNlIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoIm1pY2UiKQ0KbGlicmFyeShtaWNlKQ0KfQ0KaWYgKCFyZXF1aXJlKCJjYXJldCIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJjYXJldCIpDQpsaWJyYXJ5KGNhcmV0KQ0KfQ0KDQojIGNvZGUgY2h1bmsgc3BlY2lmaWVzIHdoZXRoZXIgdGhlIFIgY29kZSwgd2FybmluZ3MsIGFuZCBvdXRwdXQgd2lsbCBiZSBpbmNsdWRlZCBpbiB0aGUgb3V0cHV0IGZpbGVzLg0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KA0KICAjIGluY2x1ZGUgY29kZSBjaHVuayBpbiB0aGUgb3V0cHV0IGZpbGUNCiAgZWNobyA9IFRSVUUsDQogICMgc29tZXRpbWVzLCB5b3UgY29kZSBtYXkgcHJvZHVjZSB3YXJuaW5nIG1lc3NhZ2VzLCB5b3UgY2FuIGNob29zZSB0byBpbmNsdWRlIHRoZSB3YXJuaW5nIG1lc3NhZ2VzIGluIHRoZSBvdXRwdXQgZmlsZS4NCiAgd2FybmluZyA9IEZBTFNFLCANCiAgIyB5b3UgY2FuIGFsc28gZGVjaWRlIHdoZXRoZXIgdG8gaW5jbHVkZSB0aGUgb3V0cHV0IGluIHRoZSBvdXRwdXQgZmlsZS4NCiAgcmVzdWx0cyA9IFRSVUUsIA0KICBtZXNzYWdlID0gRkFMU0UsDQogIGNvbW1lbnQgPSBOQQ0KKSAgDQpgYGANCg0KDQpgYGB7cn0NCiMgPT09PT09PT09PT09PT09PT09PT0NCiMgREFUQSBJTlRBS0UNCiMgUmVhZGluZyBpbiByYXcgQ1NWIGRhdGENCiMgPT09PT09PT09PT09PT09PT09PT0NCg0KbG9hbl9kZWZhdWx0IDwtIHJlYWQuY3N2KCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vc2FsdHdhdGVyc291cC9NeUNWL3JlZnMvaGVhZHMvbWFpbi9EYXRhJTIwU2V0cy9Mb2FuJTIwRGVmYXVsdCUyMERhdGEvQmFua0xvYW5EZWZhdWx0RGF0YXNldC5jc3YiKQ0KDQojIENvcHlpbmcgbG9hbl9kZWZhdWx0DQpsb2FuX2RlZmF1bHRfTW9kMDEgPC0gbG9hbl9kZWZhdWx0DQoNCiMgTGlzdCBvZiBzdGFydGluZyB2YXJpYWJsZXMNClJhd1ZhcnMgPC0gbmFtZXMobG9hbl9kZWZhdWx0KQ0KYGBgDQoNCmBgYHtyfQ0KIyA9PT09PT09PT09PT09PT09PT09PQ0KIyBHRU5FUkFUSU5HIENBVEVHT1JJQ0FMIFZBUklBQkxFUyBGT1IgQklOQVJZIFZBUklBQkxFUw0KIyBHZW5lcmF0aW5nIExvYW5fVHlwZQ0KIyBHZW5lcmF0aW5nIERlZmF1bHRfWU4NCiMgbG9hbl9kZWZhdWx0X01vZDAxIGlzIGEgY29weSBvZiBsb2FuX2RlZmF1bHQgdGhhdCBoYXMgYmluYXJ5IHZhcmlhYmxlcyByZWZsZWN0ZWQgYXMgY2F0ZWdvcmljYWwgdmFyaWFibGVzDQojID09PT09PT09PT09PT09PT09PT09DQoNCiMgQ3JlYXRpbmcgdmFyaWFibGUgTG9hbl9UeXBlIHdoaWNoIHJlZmxlY3RzIENhcl9sb2FuLCBQZXJzb25hbF9sb2FuLCBIb21lX2xvYW4sIGFuZCBFZHVjYXRpb25fbG9hbiBhcyBvbmUgY2F0ZWdvcmljYWwgdmFyaWFibGUuDQpsb2FuX2RlZmF1bHRfTW9kMDEgPC0gbG9hbl9kZWZhdWx0X01vZDAxICU+JSANCiAgbXV0YXRlKA0KICAgIExvYW5fVHlwZSA9IGNhc2Vfd2hlbigNCiAgICAgIENhcl9sb2FuID09IDEgfiAiQ2FyIiwNCiAgICAgIFBlcnNvbmFsX2xvYW4gPT0gMSB+ICJQZXJzb25hbCIsDQogICAgICBIb21lX2xvYW4JPT0gMSB+ICJIb21lIiwNCiAgICAgIEVkdWNhdGlvbl9sb2FuID09IDEgfiAiRWR1Y2F0aW9uIiwNCiAgICAgIFRSVUUgfiAiT3RoZXIiDQogICAgKQ0KICApDQoNCiMgQ3JlYXRpbmcgdmFyaWFibGUgRGVmYXVsdF9ZTiB3aGljaCByZWZsZWN0cyBEZWZhdWx0IGFzIGEgY2F0ZWdvcmljYWwgdmFyaWFibGUuDQpsb2FuX2RlZmF1bHRfTW9kMDEgPC0gbG9hbl9kZWZhdWx0X01vZDAxICU+JSANCiAgbXV0YXRlKA0KICAgIERlZmF1bHRfWU4gPSBjYXNlX3doZW4oDQogICAgICBEZWZhdWx0ID09IDAgfiAiTm8gRGVmYXVsdCIsDQogICAgICBEZWZhdWx0ID09IDEgfiAiRGVmYXVsdGVkIiwNCiAgICAgIFRSVUUgfiBOQQ0KICAgICkNCiAgKQ0KYGBgDQoNCmBgYHtyfQ0KIyA9PT09PT09PT09PT09PT09PT09PQ0KIyBDQVBJVEFMSVpJTkcgQUxMIENBVEVHT1JJQ0FMIFZBUklBQkxFUw0KIyBCeSBkZWZhdWx0LCBzb21lIG9mIHRoZSBjaGFyYWN0ZXIgcmVzcG9uc2VzIGFyZSBsb3dlcmNhc2UNCiMgbG9hbl9kZWZhdWx0X01vZDAyIGlzIGEgY29weSBvZiBsb2FuX2RlZmF1bHRfTW9kMDEgdGhhdCBoYXMgY2hhcmFjdGVyIGVudHJpZXMgd2l0aCB1cHBlcmNhc2UgZmlyc3QgbGV0dGVycw0KIyA9PT09PT09PT09PT09PT09PT09PQ0KDQojIENvcHlpbmcgbG9hbl9kZWZhdWx0X01vZDAxDQpsb2FuX2RlZmF1bHRfTW9kMDIgPC0gbG9hbl9kZWZhdWx0X01vZDAxDQoNCiMgU2VsZWN0aW5nIGNoYXJhY3RlciB2YXJpYWJsZXMgYW5kIG1ha2luZyB0aGVpciBmaXJzdCBsZXR0ZXJzIHVwcGVyY2FzZQ0KbG9hbl9kZWZhdWx0X01vZDAyW3NhcHBseShsb2FuX2RlZmF1bHRfTW9kMDIsIHR5cGVvZikgPT0gImNoYXJhY3RlciJdIDwtIA0KICBsb2FuX2RlZmF1bHRfTW9kMDJbc2FwcGx5KGxvYW5fZGVmYXVsdF9Nb2QwMiwgdHlwZW9mKSA9PSAiY2hhcmFjdGVyIl0gJT4lIA0KICBzYXBwbHkoDQogICAgZnVuY3Rpb24oeCkgew0KICAgICAgc3Vic3RyKHgsIDEsIDEpIDwtIHRvdXBwZXIoc3Vic3RyKHgsIDEsIDEpKQ0KICAgICAgeA0KICAgIH0NCiAgKQ0KYGBgDQoNCmBgYHtyfQ0KIyA9PT09PT09PT09PT09PT09PT09PQ0KIyBEQVRBIE1PRElGSUNBVElPTg0KIyBBZGRpbmcgbWlzc2luZyB2YWx1ZXMNCiMgbG9hbl9kZWZhdWx0X01vZDAzIGlzIGEgY29weSBvZiBsb2FuX2RlZmF1bHRfTW9kMDIgd2l0aCBtaXNzaW5nIHZhbHVlcw0KIyBsb2FuX2RlZmF1bHRfTWlzc0xvYyBzaG93cyB0aGUgbG9jYXRpb25zIG9mIGFsbCBtaXNzaW5nIHZhbHVlcyB2aWEgVFJVRQ0KIyBsb2FuX2RlZmF1bHRfVHJ1ZSBzaG93cyBhbGwgb2JzZXJ2YXRpb25zIGZyb20gbG9hbl9kZWZhdWx0X01vZDAyIHRoYXQgd2VyZSBnaXZlbiBtaXNzaW5nIHZhbHVlcyBpbiBsb2FuX2RlZmF1bHRfTW9kMDMNCiMgPT09PT09PT09PT09PT09PT09PT0NCg0KIyBDb3B5aW5nIGxvYW5fZGVmYXVsdF9Nb2QwMg0KbG9hbl9kZWZhdWx0X01vZDAzIDwtIGxvYW5fZGVmYXVsdF9Nb2QwMg0KDQojIENyZWF0aW5nIHJhbmRvbSBvYnNlcnZhdGlvbiBJRHMgYW5kIHJlcGxhY2luZyB0aGUgY29ycmVzcG9uZGluZyBvYnNlcnZhdGlvbnMgd2l0aCBtaXNzaW5nDQoNCiMgbG9hbl9kZWZhdWx0X01vZDAzJENoZWNraW5nX0Ftb3VudFtzYW1wbGUoMToxMDAwLCAxMDAsIHJlcGxhY2UgPSBGQUxTRSldIDwtIE5BDQojIGxvYW5fZGVmYXVsdF9Nb2QwMyRUZXJtW3NhbXBsZSgxOjEwMDAsIDEwMCwgcmVwbGFjZSA9IEZBTFNFKV0gPC0gTkENCiMgbG9hbl9kZWZhdWx0X01vZDAzJENyZWRpdF9zY29yZVtzYW1wbGUoMToxMDAwLCAxMDAsIHJlcGxhY2UgPSBGQUxTRSldIDwtIE5BDQpsb2FuX2RlZmF1bHRfTW9kMDMkR2VuZGVyW3NhbXBsZSgxOjEwMDAsIDY4LCByZXBsYWNlID0gRkFMU0UpXSA8LSBOQQ0KbG9hbl9kZWZhdWx0X01vZDAzJE1hcml0YWxfc3RhdHVzW3NhbXBsZSgxOjEwMDAsIDg3LCByZXBsYWNlID0gRkFMU0UpXSA8LSBOQQ0KIyBsb2FuX2RlZmF1bHRfTW9kMDMkQ2FyX2xvYW5bc2FtcGxlKDE6MTAwMCwgMTAwLCByZXBsYWNlID0gRkFMU0UpXSA8LSBOQQ0KIyBsb2FuX2RlZmF1bHRfTW9kMDMkUGVyc29uYWxfbG9hbltzYW1wbGUoMToxMDAwLCAxMDAsIHJlcGxhY2UgPSBGQUxTRSldIDwtIE5BDQojIGxvYW5fZGVmYXVsdF9Nb2QwMyRIb21lX2xvYW5bc2FtcGxlKDE6MTAwMCwgMTAwLCByZXBsYWNlID0gRkFMU0UpXSA8LSBOQQ0KIyBsb2FuX2RlZmF1bHRfTW9kMDMkRWR1Y2F0aW9uX2xvYW5bc2FtcGxlKDE6MTAwMCwgMTAwLCByZXBsYWNlID0gRkFMU0UpXSA8LSBOQQ0KbG9hbl9kZWZhdWx0X01vZDAzJEVtcF9zdGF0dXNbc2FtcGxlKDE6MTAwMCwgMTM2LCByZXBsYWNlID0gRkFMU0UpXSA8LSBOQQ0KIyBsb2FuX2RlZmF1bHRfTW9kMDMkQW1vdW50W3NhbXBsZSgxOjEwMDAsIDEwMCwgcmVwbGFjZSA9IEZBTFNFKV0gPC0gTkENCiMgbG9hbl9kZWZhdWx0X01vZDAzJFNhdmluZ19hbW91bltzYW1wbGUoMToxMDAwLCAxMDAsIHJlcGxhY2UgPSBGQUxTRSldIDwtIE5BDQpsb2FuX2RlZmF1bHRfTW9kMDMkRW1wX2R1cmF0aW9uW3NhbXBsZSgxOjEwMDAsIDIwMSwgcmVwbGFjZSA9IEZBTFNFKV0gPC0gTkENCmxvYW5fZGVmYXVsdF9Nb2QwMyRBZ2Vbc2FtcGxlKDE6MTAwMCwgMTU5LCByZXBsYWNlID0gRkFMU0UpXSA8LSBOQQ0KIyBsb2FuX2RlZmF1bHRfTW9kMDMkTm9fb2ZfY3JlZGl0X2FjY291bnRbc2FtcGxlKDE6MTAwMCwgMTAwLCByZXBsYWNlID0gRkFMU0UpXSA8LSBOQQ0KDQojIFNob3dpbmcgdGhlIGxvY2F0aW9ucyBvZiBhbGwgbWlzc2luZyB2YWx1ZXMgdmlhIFRSVUUNCmxvYW5fZGVmYXVsdF9NaXNzTG9jIDwtIHNhcHBseShsb2FuX2RlZmF1bHRfTW9kMDMsIGlzLm5hKQ0KDQojIFNob3dpbmcgYWxsIHJvd3Mgb2YgY29tcGxldGUgZGF0YSB0aGF0IHdlcmUgZ2l2ZW4gbWlzc2luZyB2YWx1ZXMNCmxvYW5fZGVmYXVsdF9UcnVlIDwtIGxvYW5fZGVmYXVsdF9Nb2QwMlsocm93U3VtcyhzYXBwbHkobG9hbl9kZWZhdWx0X01vZDAzLCBpcy5uYSkpID4gMCksIF0NCmBgYA0KDQpgYGB7cn0NCiMgPT09PT09PT09PT09PT09PT09PT0NCiMgQ0FURUdPUklaSU5HIFZBUklBQkxFUw0KIyBDYXRlZ29yaXppbmcgdmFyaWFibGVzIGluIGxvYW5fZGVmYXVsdF9Nb2QwMyBpbnRvIGJpbmFyeSwgbnVtZXJpYywgYW5kIGNhdGVnb3JpY2FsDQojID09PT09PT09PT09PT09PT09PT09DQoNCiMgSWRlbnRpZnlpbmcgdmFyaWFibGUgdHlwZXMNCmxvYW5fZGVmYXVsdF9WYXJUeXBlIDwtIA0KICBsb2FuX2RlZmF1bHRfTW9kMDMgJT4lIA0KICBzYXBwbHkodHlwZW9mKQ0KDQojIElkZW50aWZ5aW5nIGJpbmFyeSB2YXJpYWJsZXMNCiMgVmFyaWFibGVzIHRoYXQgY29udGFpbiAwLCAxLCBvciBOQQ0KbG9hbl9kZWZhdWx0X1ZhclR5cGVbDQogIGFwcGx5KA0KICAgIGxvYW5fZGVmYXVsdF9Nb2QwMywgDQogICAgMiwgDQogICAgZnVuY3Rpb24oeCl7YWxsKG1hdGNoKHgsIGMoMCwgMSwgTkEpLCBub21hdGNoID0gRkFMU0UpKX0NCiAgKQ0KXSA8LSAiQmluYXJ5Ig0KDQojIElkZW50aWZ5aW5nIG51bWVyaWMgdmFyaWFibGVzDQojIFZhcmlhYmxlcyB0aGF0IGFyZSBudW1iZXJzIGJ1dCBub3QgYmluYXJ5DQpsb2FuX2RlZmF1bHRfVmFyVHlwZVtsb2FuX2RlZmF1bHRfVmFyVHlwZSA9PSAiaW50ZWdlciJdIDwtICJOdW1lcmljIg0KDQojIElkZW50aWZ5aW5nIGNhdGVnb3JpY2FsIHZhcmlhYmxlcw0KIyBWYXJpYWJsZXMgdGhhdCBhcmUgY2hhcmFjdGVycw0KbG9hbl9kZWZhdWx0X1ZhclR5cGVbbG9hbl9kZWZhdWx0X1ZhclR5cGUgPT0gImNoYXJhY3RlciJdIDwtICJDYXRlZ29yaWNhbCINCmBgYA0KDQojIEludHJvZHVjdGlvbg0KDQpXaXRoIHJlYWwtd29ybGQgZGF0YSwgaXQgaXMgY29tbW9uIHRvIGVuY291bnRlciBtaXNzaW5nIHZhbHVlcyByZXN1bHRpbmcgZnJvbSBlbnRyeSBlcnJvciwgbm9uLXJlc3BvbnNlLCBoYXJkd2FyZSBmYWlsdXJlLCBldGMuIEluIHNvbWUgY2FzZXMsIGlnbm9yaW5nIG1pc3NpbmcgdmFsdWVzIG1pZ2h0IG5vdCBtYWtlIGEgc2lnbmlmaWNhbnQgaW1wYWN0IGluIGEgc3R1ZHkncyBjb25jbHVzaW9ucywgYnV0IGluIG90aGVyIGNhc2VzLCBpZ25vcmluZyBtaXNzaW5nIHZhbHVlcyBjYW4gbGVhZCB0byBzaWduaWZpY2FudCBkZXZpYXRpb25zIGZyb20gdGhlIHRydXRoIG9yIGV2ZW4gZmFsc2UgY29uY2x1c2lvbnMuIA0KDQpUaGlzIHByb2dyYW0gc2VydmVzIGFzIGFuIGV4ZXJjaXNlIGluIGFkZHJlc3NpbmcgbWlzc2luZyBkYXRhIGJ5IHVzaW5nIGBCYW5rTG9hbkRlZmF1bHREYXRhc2V0LmNzdmAgYXMgaXRzIGJhc2UuIEJlY2F1c2UgYEJhbmtMb2FuRGVmYXVsdERhdGFzZXQuY3N2YCBoYXMgbm8gbWlzc2luZyB2YWx1ZXMgdG8gc3RhcnQgd2l0aCwgc29tZSB2YWx1ZXMgd2VyZSByZXBsYWNlZCB3aXRoIGBOQWAgdG8gc2ltdWxhdGUgYW4gaW5jb21wbGV0ZSBkYXRhc2V0Lg0KDQpUaGUgZm9sbG93aW5nIHBsb3RzIHNob3cgd2hpY2ggdmFyaWFibGVzIGNvbnRhaW4gbWlzc2luZyB2YWx1ZXMgYW5kIGhvdyBtYW55LiBIb3ZlcmluZyBvdmVyIGVhY2ggYmFyIGdpdmVzIGZ1cnRoZXIgZGV0YWlscy4NCg0KYGBge3J9DQojID09PT09PT09PT09PT09PT09PT09DQojIFBMT1RUSU5HIE1JU1NJTkcgVkFMVUVTDQojID09PT09PT09PT09PT09PT09PT09DQoNCiMgR2VuZXJhdGluZyBkYXRhIGZyYW1lIG9mIG1pc3NpbmcgdmFsdWVzIHBlciB2YXJpYWJsZQ0KTWlzc0RhdENvdW50cyA8LSBkYXRhLmZyYW1lKA0KICBWYXJpYWJsZXMgPSBuYW1lcyhsb2FuX2RlZmF1bHRfTW9kMDMpLA0KICBWYXJUeXBlID0gbG9hbl9kZWZhdWx0X1ZhclR5cGUsDQogIE1pc3NpbmcgPSBjb2xTdW1zKGlzLm5hKGxvYW5fZGVmYXVsdF9Nb2QwMykpDQopDQoNCiMgR2VuZXJhdGluZyBpbnRlcmFjdGl2ZSBwbG90IHVzaW5nIHBsb3RseQ0KUGxvdF9NaXNzaW5nVmFscyA8LSANCiAgIyBUYWtpbmcgYSBzdWJzZXQgb2YgTWlzc0RhdENvdW50cywgc28gb25seSBlbnRyaWVzIHdpdGggPiAwIG1pc3NpbmcgdmFsdWVzIHdpbGwgYmUgZGlzcGxheWVkDQogIHN1YnNldChNaXNzRGF0Q291bnRzLCBNaXNzaW5nID4gMCkgJT4lIA0KICAjIFBhc3NpbmcgdGhlIHN1YnNldCB0byBwbG90X2x5DQogIHBsb3RfbHkoDQogICAgeCA9IH5WYXJpYWJsZXMsDQogICAgeSA9IH5NaXNzaW5nLA0KICAgIHNwbGl0ID0gflZhclR5cGUsDQogICAgaG92ZXJ0ZW1wbGF0ZSA9IH5wYXN0ZTAoDQogICAgICAiPGI+Q291bnQ8L2I+OiAiLCBNaXNzaW5nLCAiIG9mIDEwMDA8YnI+IiwNCiAgICAgICI8Yj5cVTAwMjUgTWlzc2luZzwvYj46ICIsIHJvdW5kKE1pc3NpbmcgLyAxMDAwICogMTAwLCBkaWdpdHMgPSAzKSwgIlxVMDAyNSINCiAgICApDQogICkgJT4lIA0KICBsYXlvdXQoDQogICAgdGl0bGUgPSBsaXN0KA0KICAgICAgdGV4dCA9ICJNaXNzaW5nIFZhbHVlcyBwZXIgVmFyaWFibGUiDQogICAgKSwNCiAgICB4YXhpcyA9IGxpc3QoDQogICAgICB0aXRsZSA9ICJWYXJpYWJsZXMgd2l0aCBNaXNzaW5nIFZhbHVlcyIsDQogICAgICBjYXRlZ29yeW9yZGVyID0gInRyYWNlIg0KICAgICksDQogICAgeWF4aXMgPSBsaXN0KA0KICAgICAgdGl0bGUgPSAiTnVtYmVyIG9mIE1pc3NpbmcgVmFsdWVzIg0KICAgICksDQogICAgbGVnZW5kID0gbGlzdCgNCiAgICAgIHRpdGxlID0gbGlzdCh0ZXh0ID0gIjxiPiBWYXJpYWJsZSBUeXBlIDwvYj4iKSwNCiAgICAgIGJnY29sb3IgPSAiI0UyRTJFMiIsDQogICAgICBib3JkZXJjb2xvciA9ICIjRkZGRkZGIiwNCiAgICAgIGJvcmRlcndpZHRoID0gMg0KICAgICkNCiAgKQ0KDQojIE91dHB1dHRpbmcgcGxvdA0KUGxvdF9NaXNzaW5nVmFscw0KYGBgDQoNCmBgYHtyfQ0KIyA9PT09PT09PT09PT09PT09PT09PQ0KIyBQTE9UVElORyBDQVRFR09SSUNBTCBWQVJJQUJMRVMgV0lUSCBNSVNTSU5HIFZBTFVFUw0KIyA9PT09PT09PT09PT09PT09PT09PQ0KDQojIFN1YnNldHRpbmcgbG9hbl9kZWZhdWx0X01vZDAzIGludG8gdGhlIHZhcmlhYmxlcyB0aGF0IGFyZSBjYXRlZ29yaWNhbCBhbmQgaGF2ZSBtaXNzaW5nIGRhdGENCkNhdFZhcnNfTWlzcyA8LSANCiAgbG9hbl9kZWZhdWx0X01vZDAzWw0KICAgIChsb2FuX2RlZmF1bHRfVmFyVHlwZSA9PSAiQ2F0ZWdvcmljYWwiKSAmIChjb2xTdW1zKGlzLm5hKGxvYW5fZGVmYXVsdF9Nb2QwMykpID4gMCkNCiAgXQ0KDQpGaWdfRGF0YSA8LSBjKCkNCg0KZm9yKGkgaW4gMTpuY29sKENhdFZhcnNfTWlzcykpIHsNCiAgRmlnX0RhdGFbW2ldXSA8LSBDYXRWYXJzX01pc3MgJT4lIA0KICAgIGNvdW50KC5kYXRhW1tuYW1lcyhDYXRWYXJzX01pc3MpW2ldXV0pDQp9DQoNCiMgUHJlcGFyaW5nIGEgbGlzdCBvZiBzdWJwbG90cw0KTnVtRmlnIDwtIGMoKQ0KIyBVc2luZyBhIGZvciBsb29wIHRvIGdlbmVyYXRlIGEgc3VicGxvdCBwZXIgdmFyaWFibGUgaW4gQ2F0VmFyc19NaXNzDQpmb3IoaSBpbiAxOmxlbmd0aChGaWdfRGF0YSkpew0KICBOdW1GaWdbW2ldXSA8LSBwbG90X2x5KCkNCiAgZm9yKGogaW4gMTpucm93KEZpZ19EYXRhW1tpXV0pKSB7DQogICAgTnVtRmlnW1tpXV0gPC0gTnVtRmlnW1tpXV0gJT4lIA0KICAgICAgYWRkX3RyYWNlKA0KICAgICAgICB4ID0gbmFtZXMoQ2F0VmFyc19NaXNzKVtpXSwNCiAgICAgICAgeSA9IEZpZ19EYXRhW1tpXV1baiwyXSwgDQogICAgICAgIHR5cGUgPSAiYmFyIiwNCiAgICAgICAgbmFtZSA9IEZpZ19EYXRhW1tpXV1baiwxXSwNCiAgICAgICAgbGVnZW5kZ3JvdXAgPSBuYW1lcyhDYXRWYXJzX01pc3MpW2ldLA0KICAgICAgICBsZWdlbmRncm91cHRpdGxlID0gbGlzdCh0ZXh0ID0gbmFtZXMoQ2F0VmFyc19NaXNzKVtpXSksDQogICAgICAgIGhvdmVydGVtcGxhdGUgPSBwYXN0ZTAoDQogICAgICAgICAgIjxiPkNvdW50PC9iPjogIiwgRmlnX0RhdGFbW2ldXVtqLDJdLCAiIG9mIDEwMDA8YnI+IiwNCiAgICAgICAgICAiPGI+UGVyY2VudGFnZTwvYj46ICIsIHJvdW5kKEZpZ19EYXRhW1tpXV1baiwyXSAvIDEwMDAgKiAxMDAsIGRpZ2l0cyA9IDMpLCAiXFUwMDI1Ig0KICAgICAgICApDQogICAgICApICU+JSANCiAgICAgIGxheW91dCgNCiAgICAgICAgeWF4aXMgPSBsaXN0KHJhbmdlID0gYygwLCA3MDApKQ0KICAgICAgKQ0KICB9DQp9DQoNCkZpZ19DYXRWYXJzX01pc3MgPC0gDQogIHN1YnBsb3QoTnVtRmlnW1sxXV0sIE51bUZpZ1tbMl1dLCBOdW1GaWdbWzNdXSwgbnJvd3MgPSAxLCBtYXJnaW4gPSAwLjA1KSAlPiUgDQogIGxheW91dCgNCiAgICB0aXRsZSA9ICJDYXRlZ29yaWNhbCBWYXJpYWJsZXMgd2l0aCBNaXNzaW5nIFZhbHVlcyIsDQogICAgbGVnZW5kID0gbGlzdCgNCiAgICAgIHRpdGxlID0gbGlzdCh0ZXh0ID0gIjxiPiBDYXRlZ29yaWNhbCA8YnI+IFJlc3BvbnNlcyA8L2I+IiksDQogICAgICBiZ2NvbG9yID0gIiNFMkUyRTIiLA0KICAgICAgYm9yZGVyY29sb3IgPSAiI0ZGRkZGRiIsDQogICAgICBib3JkZXJ3aWR0aCA9IDINCiAgICApLA0KICAgIHlheGlzID0gbGlzdCgNCiAgICAgIHRpdGxlID0gIk51bWJlciBvZiBFbnRyaWVzIg0KICAgICkNCiAgKQ0KDQpGaWdfQ2F0VmFyc19NaXNzDQpgYGANCg0KYGBge3J9DQojID09PT09PT09PT09PT09PT09PT09DQojIFBMT1RUSU5HIE5VTUVSSUMgVkFSSUFCTEVTIFdJVEggTUlTU0lORyBWQUxVRVMNCiMgPT09PT09PT09PT09PT09PT09PT0NCg0KIyBTdWJzZXR0aW5nIGxvYW5fZGVmYXVsdF9Nb2QwMyBpbnRvIHRoZSB2YXJpYWJsZXMgdGhhdCBhcmUgY2F0ZWdvcmljYWwgYW5kIGhhdmUgbWlzc2luZyBkYXRhDQpOdW1WYXJzX01pc3MgPC0gDQogIGxvYW5fZGVmYXVsdF9Nb2QwM1sNCiAgICAobG9hbl9kZWZhdWx0X1ZhclR5cGUgPT0gIk51bWVyaWMiKSAmIChjb2xTdW1zKGlzLm5hKGxvYW5fZGVmYXVsdF9Nb2QwMykpID4gMCkNCiAgXSANCg0KTnVtVmFyc19NaXNzX01vZDAxIDwtIE51bVZhcnNfTWlzcyAlPiUgDQogIHNhcHBseShpcy5uYSkgJT4lIA0KICBpZmVsc2UoTkEsICJOb24tbWlzc2luZyIpICU+JSANCiAgZGF0YS5mcmFtZSgpDQoNCkZpZ19EYXRhIDwtIGMoKQ0KDQpmb3IoaSBpbiAxOm5jb2woTnVtVmFyc19NaXNzX01vZDAxKSkgew0KICBGaWdfRGF0YVtbaV1dIDwtIE51bVZhcnNfTWlzc19Nb2QwMSAlPiUgDQogICAgY291bnQoLmRhdGFbW25hbWVzKE51bVZhcnNfTWlzc19Nb2QwMSlbaV1dXSkNCn0NCg0KIyBQcmVwYXJpbmcgYSBsaXN0IG9mIHN1YnBsb3RzDQpOdW1GaWcgPC0gYygpDQojIFVzaW5nIGEgZm9yIGxvb3AgdG8gZ2VuZXJhdGUgYSBzdWJwbG90IHBlciB2YXJpYWJsZSBpbiBOdW1WYXJzX01pc3NfTW9kMDENCmZvcihpIGluIDE6bGVuZ3RoKEZpZ19EYXRhKSl7DQogIE51bUZpZ1tbaV1dIDwtIHBsb3RfbHkoKQ0KICBmb3IoaiBpbiAxOm5yb3coRmlnX0RhdGFbW2ldXSkpIHsNCiAgICBOdW1GaWdbW2ldXSA8LSBOdW1GaWdbW2ldXSAlPiUgDQogICAgICBhZGRfdHJhY2UoDQogICAgICAgIHggPSBuYW1lcyhOdW1WYXJzX01pc3NfTW9kMDEpW2ldLA0KICAgICAgICB5ID0gRmlnX0RhdGFbW2ldXVtqLDJdLCANCiAgICAgICAgdHlwZSA9ICJiYXIiLA0KICAgICAgICBuYW1lID0gRmlnX0RhdGFbW2ldXVtqLDFdLA0KICAgICAgICBsZWdlbmRncm91cCA9IG5hbWVzKE51bVZhcnNfTWlzc19Nb2QwMSlbaV0sDQogICAgICAgIGxlZ2VuZGdyb3VwdGl0bGUgPSBsaXN0KHRleHQgPSBuYW1lcyhOdW1WYXJzX01pc3NfTW9kMDEpW2ldKSwNCiAgICAgICAgaG92ZXJ0ZW1wbGF0ZSA9IHBhc3RlMCgNCiAgICAgICAgICAiPGI+Q291bnQ8L2I+OiAiLCBGaWdfRGF0YVtbaV1dW2osMl0sICIgb2YgMTAwMDxicj4iLA0KICAgICAgICAgICI8Yj5QZXJjZW50YWdlPC9iPjogIiwgcm91bmQoRmlnX0RhdGFbW2ldXVtqLDJdIC8gMTAwMCAqIDEwMCwgZGlnaXRzID0gMyksICJcVTAwMjUiDQogICAgICAgICkNCiAgICAgICkgJT4lIA0KICAgICAgbGF5b3V0KA0KICAgICAgICB5YXhpcyA9IGxpc3QocmFuZ2UgPSBjKDAsIDg1MCkpDQogICAgICApDQogIH0NCn0NCg0KRmlnX051bVZhcnNfTWlzcyA8LSANCiAgc3VicGxvdChOdW1GaWdbWzFdXSwgTnVtRmlnW1syXV0sIG5yb3dzID0gMSwgbWFyZ2luID0gMC4wNSkgJT4lIA0KICBsYXlvdXQoDQogICAgdGl0bGUgPSAiTnVtZXJpY2FsIFZhcmlhYmxlcyB3aXRoIE1pc3NpbmcgVmFsdWVzIiwNCiAgICBsZWdlbmQgPSBsaXN0KA0KICAgICAgdGl0bGUgPSBsaXN0KHRleHQgPSAiPGI+IFJlc3BvbnNlcyA8L2I+IiksDQogICAgICBiZ2NvbG9yID0gIiNFMkUyRTIiLA0KICAgICAgYm9yZGVyY29sb3IgPSAiI0ZGRkZGRiIsDQogICAgICBib3JkZXJ3aWR0aCA9IDINCiAgICApLA0KICAgIHlheGlzID0gbGlzdCgNCiAgICAgIHRpdGxlID0gIk51bWJlciBvZiBFbnRyaWVzIg0KICAgICkNCiAgKQ0KDQpGaWdfTnVtVmFyc19NaXNzDQpgYGANCg0KIyMgSW1wdXRhdGlvbg0KDQpJbXB1dGF0aW9uIGlzIHRoZSBwcm9jZXNzIG9mIHJlcGxhY2luZyBtaXNzaW5nIGRhdGEgd2l0aCBzdWJzdGl0dXRlIHZhbHVlcy4gSWRlYWxseSwgd2Ugd2FudCB0byBpbXB1dGUgdmFsdWVzIGFzIGNsb3NlIGFzIHBvc3NpYmxlIHRvIHRoZSB0cnVlIHZhbHVlIHRoYXQgaXMgbWlzc2luZy4gVG8gdGhhdCBlbmQsIHdlIG11c3QgdHJ5IHRvIHByZWRpY3Qgd2hhdCB0aGUgbWlzc2luZyB2YWx1ZSB3b3VsZCBoYXZlIGJlZW4uDQoNCldlIHdpbGwgZXhwbG9yZSB0aGUgZm9sbG93aW5nIG1ldGhvZHMgb2YgaW1wdXRhdGlvbjoNCg0KKiBrLU5lYXJlc3QgTmVpZ2hib3JzICgqayotTk4pIGZvciBjYXRlZ29yaWNhbCB2YXJpYWJsZXMgd2l0aCBtaXNzaW5nIHZhbHVlcw0KKiBSYW5kb20gcmVncmVzc2lvbiBpbXB1dGF0aW9uIGZvciBudW1lcmljYWwgdmFyaWFibGVzIHdpdGggbWlzc2luZyB2YWx1ZXMNCiogTXVsdGlwbGUgSW1wdXRhdGlvbiBieSBDaGFpbmVkIEVxdWF0aW9ucyAoTUlDRSkgZm9yIGFsbCBtaXNzaW5nIHZhbHVlcw0KDQojIyBGZWF0dXJlIEVuZ2luZWVyaW5nDQoNCkZlYXR1cmUgZW5naW5lZXJpbmcgaXMgdGhlIHByb2Nlc3Mgb2Ygc2VsZWN0aW5nLCB0cmFuc2Zvcm1pbmcsIGFuZCBjcmVhdGluZyBmZWF0dXJlIHZhcmlhYmxlcyB0byBpbXByb3ZlIHRoZSBwZXJmb3JtYW5jZSBvZiBwcmVkaWN0aXZlIG1vZGVscy4NCg0KRm9yIHRoZSBwdXJwb3NlIG9mIGZlYXR1cmUgc2VsZWN0aW9uLCB3ZSB1c2UgYSB3cmFwcGVyIG1ldGhvZCwgd2hpY2ggY29uc2lzdHMgb2YgdXNpbmcgYSBwcmVkaWN0aXZlIG1vZGVsIHRvIGV2YWx1YXRlIHRoZSBwZXJmb3JtYW5jZSBvZiBkaWZmZXJlbnQgY29tYmluYXRpb25zIG9mIGZlYXR1cmVzIHRoZW4gc2VsZWN0aW5nIHRoZSBoaWdoZXN0IHBlcmZvcm1pbmcgc2V0IG9mIGZlYXR1cmVzLiBUaGUgYGNhcmV0YCBwYWNrYWdlIGVuYWJsZXMgdXMgdG8gdXNlIHJlY3Vyc2l2ZSBmZWF0dXJlIGVsaW1pbmF0aW9uIChSRkUpIHRvIG91dHB1dCB0aGUgbGlzdCBvZiBmZWF0dXJlcyB0aGF0IGdpdmUgdXMgdGhlIGJlc3QgcGVyZm9ybWFuY2UuIFRoZSBmb2xsb3dpbmcgb3V0cHV0IGdpdmVzIHVzIHRoZSBiZXN0IHNldCBvZiBmZWF0dXJlIHZhcmlhYmxlcyB0byBwcmVkaWN0IHRoZSBsYWJlbCB2YXJpYWJsZSwgYERlZmF1bHRgLg0KDQpgYGB7cn0NCiMgPT09PT09PT09PT09PT09PT09PT0NCiMgRkVBVFVSRSBTRUxFQ1RJT04NCiMgRHIuIFBlbmcncyBjb2RlIHdhcyB1c2VkIGFzIGEgcmVmZXJlbmNlDQojID09PT09PT09PT09PT09PT09PT09DQoNCnJlc3VsdHMgPC0gcmZlKA0KICBsb2FuX2RlZmF1bHRbLCAtMV0sIA0KICBsb2FuX2RlZmF1bHQkRGVmYXVsdCwgDQogIHNpemVzID0gYygxOjUpLCANCiAgcmZlQ29udHJvbCA9IHJmZUNvbnRyb2woZnVuY3Rpb25zID0gcmZGdW5jcywgbWV0aG9kID0gImN2IiwgbnVtYmVyID0gMTApDQogICkNCg0KcHJlZGljdG9ycyhyZXN1bHRzKQ0KYGBgDQoNCkFzIFJGRSBoYXMgcmV0dXJuZWQgYWxsIG9mIG91ciBvcmlnaW5hbCBmZWF0dXJlIHZhcmlhYmxlcywgd2UgcHJvY2VlZCB3aXRob3V0IGVsaW1pbmF0aW5nIGFueSBmZWF0dXJlcyBmcm9tIGZ1cnRoZXIgYW5hbHlzaXMuDQoNClZpc3VhbGl6aW5nIGFsbCBudW1lcmljYWwgbm9uLWJpbmFyeSB2YXJpYWJsZXMsIHdlIGNhbiBzZWUgdGhhdDoNCg0KKiBUaGUgbmFycm93IG51bWVyaWNhbCByYW5nZSBhbmQgc3RlZXAgZHJvcHMgb2YgYE5vX29mX2NyZWRpdF9hY2NgIG1ha2UgaXQgYSBwcmltZSBjYW5kaWRhdGUgZm9yIGJpbm5pbmcuDQoqIFRoZSByZW1haW5pbmcgbnVtZXJpY2FsIHZhcmlhYmxlcyBhcmUgY2FuZGlkYXRlcyBmb3Igc3RhbmRhcmRpemF0aW9uIHRvIGJldHRlciBzdXBwb3J0IGZ1dHVyZSBtb2RlbGluZy4NCg0KYGBge3J9DQojID09PT09PT09PT09PT09PT09PT09DQojIFBMT1RUSU5HIEFMTCBOVU1FUklDQUwgTk9OLUJJTkFSWSBWQVJJQUJMRVMNCiMgPT09PT09PT09PT09PT09PT09PT0NCg0KIyBTZWxlY3Rpbmcgb25seSBudW1lcmljIHZhcmlhYmxlcw0KTnVtVmFycyA8LSBzZWxlY3QobG9hbl9kZWZhdWx0X01vZDAzLCB3aGVyZShpcy5udW1lcmljKSkNCiMgU2VsZWN0aW5nIGVsaW1pbmF0aW5nIGFueSBiaW5hcnkgdmFyaWFibGVzDQpOdW1WYXJzIDwtIE51bVZhcnNbIWFwcGx5KE51bVZhcnMsIDIsIGZ1bmN0aW9uKHgpe2FsbChtYXRjaCh4LCBjKDAsIDEsIE5BKSwgbm9tYXRjaCA9IEZBTFNFKSl9KV0NCg0KIyBQcmVwYXJpbmcgYSBsaXN0IG9mIHN1YnBsb3RzDQpOdW1GaWcgPC0gYygpDQojIFVzaW5nIGEgZm9yIGxvb3AgdG8gZ2VuZXJhdGUgYSBzdWJwbG90IHBlciB2YXJpYWJsZSBpbiBOdW1WYXJzDQpmb3IoaSBpbiAxOmxlbmd0aChuYW1lcyhOdW1WYXJzKSkpew0KICBOdW1GaWdbW2ldXSA8LSBwbG90X2x5KA0KICAgIHggPSBOdW1WYXJzW1tpXV0sIA0KICAgICMgeSA9ICIiLCANCiAgICB0eXBlID0gImhpc3RvZ3JhbSIsDQogICAgbmFtZSA9IGNvbG5hbWVzKE51bVZhcnMpW2ldDQogICkNCn0NCg0KIyBHZW5lcmF0aW5nIGEgcGxvdCB0aGF0IGNvbnRhaW5zIDggc3VicGxvdHMgKG9uZSBmb3IgZWFjaCB2YXJpYWJsZSBpbiBOdW1WYXJzKSBhY3Jvc3MgNCByb3dzDQpQbG90X051bVZhcnMgPC0gDQogIHN1YnBsb3QoTnVtRmlnW1sxXV0sIE51bUZpZ1tbMl1dLCBOdW1GaWdbWzNdXSwgTnVtRmlnW1s0XV0sIE51bUZpZ1tbNV1dLCBOdW1GaWdbWzZdXSwgTnVtRmlnW1s3XV0sIE51bUZpZ1tbOF1dLCBucm93cyA9IDQsIG1hcmdpbiA9IDAuMDUpICU+JSANCiAgbGF5b3V0KA0KICAgIHRpdGxlID0gIkRpc3RyaWJ1dGlvbnMgb2YgQWxsIE51bWVyaWNhbCBOb24tYmluYXJ5IFZhcmlhYmxlcyIsDQogICAgbGVnZW5kID0gbGlzdCgNCiAgICAgIHRpdGxlID0gbGlzdCh0ZXh0ID0gIjxiPiBWYXJpYWJsZSA8L2I+IiksDQogICAgICBiZ2NvbG9yID0gIiNFMkUyRTIiLA0KICAgICAgYm9yZGVyY29sb3IgPSAiI0ZGRkZGRiIsDQogICAgICBib3JkZXJ3aWR0aCA9IDINCiAgICApDQogICkNCg0KIyBPdXRwdXR0aW5nIHBsb3QNClBsb3RfTnVtVmFycw0KYGBgDQoNCkJpbm5pbmcgYE5vX29mX2NyZWRpdF9hY2NgIGFjY29yZGluZyB0byB0aGUgc2hhcnAgZHJvcHMgaW4gdGhlIGhpc3RvZ3JhbSByZXN1bHRzIGluIHRoZSBjcmVhdGlvbiBvZiBuZXcgdmFyaWFibGUgYE5vX29mX2NyZWRpdF9hY2NfQmluc2Agd2l0aCB0aGUgZm9sbG93aW5nIGJyZWFrZG93bjoNCg0KYGBge3J9DQojID09PT09PT09PT09PT09PT09PT09DQojIEJJTk5JTkcgTm9fb2ZfY3JlZGl0X2FjYw0KIyA9PT09PT09PT09PT09PT09PT09PQ0KDQojIFRyYW5zZm9ybWluZyBOb19vZl9jcmVkaXRfYWNjIGludG8gb3JkaW5hbCBjYXRlZ29yaWNhbCB2YXJpYWJsZXMNCmxvYW5fZGVmYXVsdF9Nb2QwNCA8LSBsb2FuX2RlZmF1bHRfTW9kMDMgJT4lIA0KICBtdXRhdGUoDQogICAgTm9fb2ZfY3JlZGl0X2FjY19CaW5zID0gY3V0KA0KICAgICAgTm9fb2ZfY3JlZGl0X2FjYywNCiAgICAgIGJyZWFrcyA9IGMoMSwgMiwgNSwgOSksDQogICAgICBpbmNsdWRlLmxvd2VzdCA9IFRSVUUNCiAgICApDQogICkNCg0KdGFibGUobG9hbl9kZWZhdWx0X01vZDA0JE5vX29mX2NyZWRpdF9hY2NfQmlucykNCmBgYA0KDQpTdGFuZGFyZGl6YXRpb24gb2YgdGhlIHJlbWFpbmluZyBzZXZlbiBudW1lcmljYWwgdmFyaWFibGVzIHJlc3VsdHMgaW4gdGhlIGZvbGxvd2luZyBkaXN0cmlidXRpb246DQoNCmBgYHtyfQ0KIyA9PT09PT09PT09PT09PT09PT09PQ0KIyBGRUFUVVJFIFNUQU5EQVJESVpBVElPTg0KIyA9PT09PT09PT09PT09PT09PT09PQ0KDQojIE5vcm1hbGl6YXRpb24gZnVuY3Rpb24gZnJvbSBEci4gUGVuZw0Kc3RhbmRhcmRpemUgPC0gZnVuY3Rpb24oeCkgew0KICByZXR1cm4oKHggLSBtZWFuKHgsIG5hLnJtID0gVFJVRSkpIC8gc2QoeCwgbmEucm0gPSBUUlVFKSkNCn0NCg0KIyBOb3JtYWxpemluZyBudW1lcmljIHZhcmlhYmxlcw0KbG9hbl9kZWZhdWx0X01vZDA0JENoZWNraW5nX2Ftb3VudF9TdGFuZCA8LSANCiAgc3RhbmRhcmRpemUobG9hbl9kZWZhdWx0X01vZDA0JENoZWNraW5nX2Ftb3VudCkNCmxvYW5fZGVmYXVsdF9Nb2QwNCRUZXJtX1N0YW5kIDwtIA0KICBzdGFuZGFyZGl6ZShsb2FuX2RlZmF1bHRfTW9kMDQkVGVybSkNCmxvYW5fZGVmYXVsdF9Nb2QwNCRDcmVkaXRfc2NvcmVfU3RhbmQgPC0gDQogIHN0YW5kYXJkaXplKGxvYW5fZGVmYXVsdF9Nb2QwNCRDcmVkaXRfc2NvcmUpDQpsb2FuX2RlZmF1bHRfTW9kMDQkQW1vdW50X1N0YW5kIDwtIA0KICBzdGFuZGFyZGl6ZShsb2FuX2RlZmF1bHRfTW9kMDQkQW1vdW50KQ0KbG9hbl9kZWZhdWx0X01vZDA0JFNhdmluZ19hbW91bnRfU3RhbmQgPC0gDQogIHN0YW5kYXJkaXplKGxvYW5fZGVmYXVsdF9Nb2QwNCRTYXZpbmdfYW1vdW50KQ0KbG9hbl9kZWZhdWx0X01vZDA0JEVtcF9kdXJhdGlvbl9TdGFuZCA8LSANCiAgc3RhbmRhcmRpemUobG9hbl9kZWZhdWx0X01vZDA0JEVtcF9kdXJhdGlvbikNCmxvYW5fZGVmYXVsdF9Nb2QwNCRBZ2VfU3RhbmQgPC0gDQogIHN0YW5kYXJkaXplKGxvYW5fZGVmYXVsdF9Nb2QwNCRBZ2UpDQoNCiMgTGlzdCBvZiBzdGFuZGFyZGl6ZWQgdmFyaWFibGVzDQpTdGFuZFZhcnMgPC0gYygiQ2hlY2tpbmdfYW1vdW50X1N0YW5kIiwgIlRlcm1fU3RhbmQiLCAiQ3JlZGl0X3Njb3JlX1N0YW5kIiwgIkFtb3VudF9TdGFuZCIsICJTYXZpbmdfYW1vdW50X1N0YW5kIiwgIkVtcF9kdXJhdGlvbl9TdGFuZCIsICJBZ2VfU3RhbmQiKQ0KDQojIE51bWVyaWMgdmFyaWFibGUgZGF0YSBmb3IgZGUtc3RhbmRhcmRpemF0aW9uDQpEZVN0YW5kVmFyRGF0YSA8LSANCiAgZGF0YS5mcmFtZSgNCiAgICBWYXJpYWJsZSA9IGMoIkNoZWNraW5nX2Ftb3VudCIsICJUZXJtIiwgIkNyZWRpdF9zY29yZSIsICJBbW91bnQiLCAiU2F2aW5nX2Ftb3VudCIsICJFbXBfZHVyYXRpb24iLCAiQWdlIiksDQogICAgTWVhbiA9IGMoDQogICAgICBtZWFuKGxvYW5fZGVmYXVsdF9Nb2QwNCRDaGVja2luZ19hbW91bnQsIG5hLnJtID0gVFJVRSksDQogICAgICBtZWFuKGxvYW5fZGVmYXVsdF9Nb2QwNCRUZXJtLCBuYS5ybSA9IFRSVUUpLA0KICAgICAgbWVhbihsb2FuX2RlZmF1bHRfTW9kMDQkQ3JlZGl0X3Njb3JlLCBuYS5ybSA9IFRSVUUpLA0KICAgICAgbWVhbihsb2FuX2RlZmF1bHRfTW9kMDQkQW1vdW50LCBuYS5ybSA9IFRSVUUpLA0KICAgICAgbWVhbihsb2FuX2RlZmF1bHRfTW9kMDQkU2F2aW5nX2Ftb3VudCwgbmEucm0gPSBUUlVFKSwNCiAgICAgIG1lYW4obG9hbl9kZWZhdWx0X01vZDA0JEVtcF9kdXJhdGlvbiwgbmEucm0gPSBUUlVFKSwNCiAgICAgIG1lYW4obG9hbl9kZWZhdWx0X01vZDA0JEFnZSwgbmEucm0gPSBUUlVFKQ0KICAgICksDQogICAgU0QgPSBjKA0KICAgICAgc2QobG9hbl9kZWZhdWx0X01vZDA0JENoZWNraW5nX2Ftb3VudCwgbmEucm0gPSBUUlVFKSwNCiAgICAgIHNkKGxvYW5fZGVmYXVsdF9Nb2QwNCRUZXJtLCBuYS5ybSA9IFRSVUUpLA0KICAgICAgc2QobG9hbl9kZWZhdWx0X01vZDA0JENyZWRpdF9zY29yZSwgbmEucm0gPSBUUlVFKSwNCiAgICAgIHNkKGxvYW5fZGVmYXVsdF9Nb2QwNCRBbW91bnQsIG5hLnJtID0gVFJVRSksDQogICAgICBzZChsb2FuX2RlZmF1bHRfTW9kMDQkU2F2aW5nX2Ftb3VudCwgbmEucm0gPSBUUlVFKSwNCiAgICAgIHNkKGxvYW5fZGVmYXVsdF9Nb2QwNCRFbXBfZHVyYXRpb24sIG5hLnJtID0gVFJVRSksDQogICAgICBzZChsb2FuX2RlZmF1bHRfTW9kMDQkQWdlLCBuYS5ybSA9IFRSVUUpDQogICAgKQ0KICApDQpgYGANCg0KYGBge3J9DQojID09PT09PT09PT09PT09PT09PT09DQojIFBMT1RUSU5HIEFMTCBTVEFOREFSRElaRUQgTlVNRVJJQ0FMIFZBUklBQkxFUw0KIyA9PT09PT09PT09PT09PT09PT09PQ0KDQojIFNlbGVjdGluZyBvbmx5IHN0YW5kYXJkaXplZCB2YXJpYWJsZXMNClN0YW5kTnVtVmFycyA8LSBsb2FuX2RlZmF1bHRfTW9kMDRbU3RhbmRWYXJzXQ0KDQojIFByZXBhcmluZyBhIGxpc3Qgb2Ygc3VicGxvdHMNCk51bUZpZyA8LSBjKCkNCiMgVXNpbmcgYSBmb3IgbG9vcCB0byBnZW5lcmF0ZSBhIHN1YnBsb3QgcGVyIHZhcmlhYmxlIGluIFN0YW5kTnVtVmFycw0KZm9yKGkgaW4gMTpsZW5ndGgobmFtZXMoU3RhbmROdW1WYXJzKSkpew0KICBOdW1GaWdbW2ldXSA8LSBwbG90X2x5KA0KICAgIHggPSBTdGFuZE51bVZhcnNbW2ldXSwgDQogICAgIyB5ID0gIiIsIA0KICAgIHR5cGUgPSAiaGlzdG9ncmFtIiwNCiAgICBuYW1lID0gY29sbmFtZXMoU3RhbmROdW1WYXJzKVtpXQ0KICApDQp9DQoNCiMgR2VuZXJhdGluZyBhIHBsb3QgdGhhdCBjb250YWlucyA4IHN1YnBsb3RzIChvbmUgZm9yIGVhY2ggdmFyaWFibGUgaW4gU3RhbmROdW1WYXJzKSBhY3Jvc3MgNCByb3dzDQpQbG90X1N0YW5kTnVtVmFycyA8LSANCiAgc3VicGxvdChOdW1GaWdbWzFdXSwgTnVtRmlnW1syXV0sIE51bUZpZ1tbM11dLCBOdW1GaWdbWzRdXSwgTnVtRmlnW1s1XV0sIE51bUZpZ1tbNl1dLCBOdW1GaWdbWzddXSwgbnJvd3MgPSA0LCBtYXJnaW4gPSAwLjA1KSAlPiUgDQogIGxheW91dCgNCiAgICB0aXRsZSA9ICJEaXN0cmlidXRpb25zIG9mIEFsbCBTdGFuZGFyZGl6ZWQgTnVtZXJpY2FsIFZhcmlhYmxlcyIsDQogICAgbGVnZW5kID0gbGlzdCgNCiAgICAgIHRpdGxlID0gbGlzdCh0ZXh0ID0gIjxiPiBWYXJpYWJsZSA8L2I+IiksDQogICAgICBiZ2NvbG9yID0gIiNFMkUyRTIiLA0KICAgICAgYm9yZGVyY29sb3IgPSAiI0ZGRkZGRiIsDQogICAgICBib3JkZXJ3aWR0aCA9IDINCiAgICApDQogICkNCg0KIyBPdXRwdXR0aW5nIHBsb3QNClBsb3RfU3RhbmROdW1WYXJzDQpgYGANCg0KIyBSZXBsYWNlbWVudCBJbXB1dGF0aW9uIGZvciBDYXRlZ29yaWNhbCBGZWF0dXJlcw0KDQpUaGUgZGF0YSBjb250YWlucyBtaXNzaW5nIHZhbHVlcyBpbiB0aGUgdmFyaWFibGVzIGBHZW5kZXJgLCBgTWFyaXRhbF9zdGF0dXNgLCBhbmQgYEVtcF9zdGF0dXNgLCBhbmQgdGhlc2UgbWlzc2luZyB2YWx1ZXMgbXVzdCBiZSBhZGRyZXNzZWQgYmVmb3JlIHdlIGNhbiBwcm9jZWVkIHdpdGggbW9kZWxpbmcuIFRvIHRoYXQgZW5kLCB3ZSB1c2UgKmsqLU5OIHRvIGltcHV0ZSB0aGUgbWlzc2luZyB2YWx1ZXMuIFRoZSByZXN1bHRzIG9mIHRoaXMgaW1wdXRhdGlvbiBjYW4gYmUgc2VlbiBpbiB0aGUgZm9sbG93aW5nIHBsb3Q6DQoNCmBgYHtyfQ0KIyA9PT09PT09PT09PT09PT09PT09PQ0KIyBJTVBVVElORyBDQVRFR09SSUNBTCBWQUxVRVMgV0lUSCBrTk4NCiMgQ2F0SW1wdXRlIGlzIGxvYW5fZGVmYXVsdF9Nb2QwNCB3aXRoIGltcHV0ZWQgY2F0ZWdvcmljYWwgdmFsdWVzDQojID09PT09PT09PT09PT09PT09PT09DQoNCkNhdEltcHV0ZSA8LSBrTk4oDQogIGxvYW5fZGVmYXVsdF9Nb2QwNFtSYXdWYXJzXSwgDQogIHZhcmlhYmxlID0gYygiR2VuZGVyIiwgIk1hcml0YWxfc3RhdHVzIiwgIkVtcF9zdGF0dXMiKSwNCiAgayA9IDUNCikNCmBgYA0KDQpgYGB7cn0NCiMgPT09PT09PT09PT09PT09PT09PT0NCiMgUExPVFRJTkcgQ0FURUdPUklDQUwgVkFSSUFCTEVTIFdJVEggSU1QVVRFRCBWQUxVRVMNCiMgPT09PT09PT09PT09PT09PT09PT0NCg0KIyBTdWJzZXR0aW5nIGxvYW5fZGVmYXVsdF9Nb2QwNCBpbnRvIHRoZSB2YXJpYWJsZXMgdGhhdCBhcmUgY2F0ZWdvcmljYWwgYW5kIGhhdmUgbWlzc2luZyBkYXRhDQpDYXRWYXJzX0ltcCA8LSBDYXRJbXB1dGVbDQogIGMoIkdlbmRlciIsICJNYXJpdGFsX3N0YXR1cyIsICJFbXBfc3RhdHVzIiwgDQogICAgIkdlbmRlcl9pbXAiLCAiTWFyaXRhbF9zdGF0dXNfaW1wIiwgIkVtcF9zdGF0dXNfaW1wIikNCl0NCg0KQ2F0VmFyc19JbXAgPC0gQ2F0VmFyc19JbXAgJT4lIA0KICBtdXRhdGUoDQogICAgR2VuZGVyID0gY2FzZV93aGVuKA0KICAgICAgR2VuZGVyX2ltcCA9PSBUUlVFIH4gcGFzdGUwKEdlbmRlciwgIiAoSW1wdXRlZCkiKSwNCiAgICAgIFRSVUUgfiBHZW5kZXINCiAgICApLA0KICAgIE1hcml0YWxfc3RhdHVzID0gY2FzZV93aGVuKA0KICAgICAgTWFyaXRhbF9zdGF0dXNfaW1wID09IFRSVUUgfiBwYXN0ZTAoTWFyaXRhbF9zdGF0dXMsICIgKEltcHV0ZWQpIiksDQogICAgICBUUlVFIH4gTWFyaXRhbF9zdGF0dXMNCiAgICApLA0KICAgIEVtcF9zdGF0dXMgPSBjYXNlX3doZW4oDQogICAgICBFbXBfc3RhdHVzX2ltcCA9PSBUUlVFIH4gcGFzdGUwKEVtcF9zdGF0dXMsICIgKEltcHV0ZWQpIiksDQogICAgICBUUlVFIH4gRW1wX3N0YXR1cw0KICAgICksDQogICkNCg0KRmlnX0RhdGEgPC0gYygpDQoNCmZvcihpIGluIDE6bmNvbChDYXRWYXJzX0ltcCkpIHsNCiAgRmlnX0RhdGFbW2ldXSA8LSBDYXRWYXJzX0ltcCAlPiUgDQogICAgY291bnQoLmRhdGFbW25hbWVzKENhdFZhcnNfSW1wKVtpXV1dKQ0KfQ0KDQojIFByZXBhcmluZyBhIGxpc3Qgb2Ygc3VicGxvdHMNCk51bUZpZyA8LSBjKCkNCiMgVXNpbmcgYSBmb3IgbG9vcCB0byBnZW5lcmF0ZSBhIHN1YnBsb3QgcGVyIHZhcmlhYmxlIGluIENhdFZhcnNfSW1wDQpmb3IoaSBpbiAxOjMpew0KICBOdW1GaWdbW2ldXSA8LSBwbG90X2x5KCkNCiAgZm9yKGogaW4gMTpucm93KEZpZ19EYXRhW1tpXV0pKSB7DQogICAgTnVtRmlnW1tpXV0gPC0gTnVtRmlnW1tpXV0gJT4lIA0KICAgICAgYWRkX3RyYWNlKA0KICAgICAgICB4ID0gbmFtZXMoQ2F0VmFyc19JbXApW2ldLA0KICAgICAgICB5ID0gRmlnX0RhdGFbW2ldXVtqLDJdLCANCiAgICAgICAgdHlwZSA9ICJiYXIiLA0KICAgICAgICBuYW1lID0gRmlnX0RhdGFbW2ldXVtqLDFdLA0KICAgICAgICBsZWdlbmRncm91cCA9IG5hbWVzKENhdFZhcnNfSW1wKVtpXSwNCiAgICAgICAgbGVnZW5kZ3JvdXB0aXRsZSA9IGxpc3QodGV4dCA9IG5hbWVzKENhdFZhcnNfSW1wKVtpXSksDQogICAgICAgIGhvdmVydGVtcGxhdGUgPSBwYXN0ZTAoDQogICAgICAgICAgIjxiPkNvdW50PC9iPjogIiwgRmlnX0RhdGFbW2ldXVtqLDJdLCAiIG9mIDEwMDA8YnI+IiwNCiAgICAgICAgICAiPGI+UGVyY2VudGFnZTwvYj46ICIsIHJvdW5kKEZpZ19EYXRhW1tpXV1baiwyXSAvIDEwMDAgKiAxMDAsIGRpZ2l0cyA9IDMpLCAiXFUwMDI1Ig0KICAgICAgICApDQogICAgICApDQogIH0NCn0NCg0KRmlnX0NhdFZhcnNfSW1wIDwtIA0KICBzdWJwbG90KE51bUZpZ1tbMV1dLCBOdW1GaWdbWzJdXSwgTnVtRmlnW1szXV0sIG5yb3dzID0gMSwgbWFyZ2luID0gMC4wNSwgc2hhcmVZID0gVFJVRSkgJT4lIA0KICBsYXlvdXQoDQogICAgdGl0bGUgPSAiQ2F0ZWdvcmljYWwgVmFyaWFibGVzIHdpdGggTWlzc2luZyBWYWx1ZXMiLA0KICAgIGxlZ2VuZCA9IGxpc3QoDQogICAgICB0aXRsZSA9IGxpc3QodGV4dCA9ICI8Yj4gQ2F0ZWdvcmljYWwgPGJyPiBSZXNwb25zZXMgPC9iPiIpLA0KICAgICAgYmdjb2xvciA9ICIjRTJFMkUyIiwNCiAgICAgIGJvcmRlcmNvbG9yID0gIiNGRkZGRkYiLA0KICAgICAgYm9yZGVyd2lkdGggPSAyDQogICAgKSwNCiAgICB5YXhpcyA9IGxpc3QoDQogICAgICB0aXRsZSA9ICJOdW1iZXIgb2YgRW50cmllcyINCiAgICApDQogICkNCg0KRmlnX0NhdFZhcnNfSW1wDQpgYGANCg0KVGhlIHJlc3VsdHMgb2YgdGhpcyBtZXRob2Qgb2YgaW1wdXRhdGlvbiByb3VnaGx5IGFwcGVhciB0byByZWZsZWN0IHRoZSBkaXN0cmlidXRpb24gb2YgdGhlIG5vbi1taXNzaW5nIGRhdGEuIFRoaXMgd291bGQgbWFrZSBzZW5zZSBpZiB0aGUgZGF0YSBpcyBtaXNzaW5nIGF0IHJhbmRvbS4NCg0KIyBSYW5kb20gUmVncmVzc2lvbi1iYXNlZCBJbXB1dGF0aW9uIGZvciBOdW1lcmljYWwgRmVhdHVyZXMNCg0KVGhlIGRhdGEgY29udGFpbnMgbWlzc2luZyB2YWx1ZXMgaW4gdGhlIHZhcmlhYmxlcyBgRW1wX2R1cmF0aW9uYCBhbmQgYEFnZWAsIGFuZCB0aGVzZSBtaXNzaW5nIHZhbHVlcyBtdXN0IGJlIGFkZHJlc3NlZCBiZWZvcmUgd2UgY2FuIHByb2NlZWQgd2l0aCBtb2RlbGluZy4gVG8gdGhhdCBlbmQsIHdlIHVzZSByYW5kb20gcmVncmVzc2lvbiB0byBpbXB1dGUgdGhlIG1pc3NpbmcgdmFsdWVzLg0KDQpXZSBzZWxlY3RlZCB0aGUgdmFyaWFibGUgdG8gcmVmZXJlbmNlIGJ5IGxvb2tpbmcgYXQgdGhlIGNvcnJlbGF0aW9uIHBsb3QgYmV0d2VlbiBudW1lcmljYWwgdmFyaWFibGVzLiBCeSBoYXZpbmcgdGhlIGhpZ2hlc3QgY29ycmVsYXRpb25zLCBgTm9fb2ZfY3JlZGl0X2FjY2Agd2FzIGNob3NlbiBmb3IgYEVtcF9kdXJhdGlvbmAgYW5kIGBTYXZpbmdfYW1vdW50YCB3YXMgY2hvc2VuIGZvciBgQWdlYC4NCg0KYGBge3J9DQojID09PT09PT09PT09PT09PT09PT09DQojIFBMT1RUSU5HIENPUlJFTEFUSU9OIEJFVFdFRU4gTlVNRVJJQ0FMIFZBUklBQkxFUw0KIyA9PT09PT09PT09PT09PT09PT09PQ0KDQpjb3JycGxvdCgNCiAgY29yKA0KICAgIGxvYW5fZGVmYXVsdF9Nb2QwNFsNCiAgICAgIGMoDQogICAgICAgICMgIkRlZmF1bHQiLA0KICAgICAgICAiQ2hlY2tpbmdfYW1vdW50IiwNCiAgICAgICAgIlRlcm0iLA0KICAgICAgICAiQ3JlZGl0X3Njb3JlIiwNCiAgICAgICAgIyAiR2VuZGVyIiwNCiAgICAgICAgIyAiTWFyaXRhbF9zdGF0dXMiLA0KICAgICAgICAjICJDYXJfbG9hbiIsDQogICAgICAgICMgIlBlcnNvbmFsX2xvYW4iLA0KICAgICAgICAjICJIb21lX2xvYW4iLA0KICAgICAgICAjICJFZHVjYXRpb25fbG9hbiIsDQogICAgICAgICMgIkVtcF9zdGF0dXMiLA0KICAgICAgICAiQW1vdW50IiwNCiAgICAgICAgIlNhdmluZ19hbW91bnQiLA0KICAgICAgICAiRW1wX2R1cmF0aW9uIiwNCiAgICAgICAgIkFnZSIsDQogICAgICAgICJOb19vZl9jcmVkaXRfYWNjIg0KICAgICAgKQ0KICAgIF0sDQogICAgdXNlID0gImNvbXBsZXRlLm9icyINCiAgKSwNCiAgdHlwZSA9ICJsb3dlciIsDQogIHRsLnNydCA9IDM1LA0KICBhZGRDb2VmLmNvbCA9ICdncmV5NTAnDQopDQpgYGANCg0KVGhlIGZvbGxvd2luZyBpcyB0aGUgcmVzdWx0IG9mIHRoZSBpbXB1dGF0aW9uOg0KDQpgYGB7cn0NCiMgPT09PT09PT09PT09PT09PT09PT0NCiMgSU1QVVRJTkcgTlVNRVJJQ0FMIFZBTFVFUyBXSVRIIExJTkVBUiBSRUdSRVNTSU9ODQojIE51bUltcHV0ZSBpcyBsb2FuX2RlZmF1bHRfTW9kMDQgd2l0aCBpbXB1dGVkIG51bWVyaWNhbCB2YWx1ZXMNCiMgRHIuIFBlbmcncyBjb2RlIHdhcyB1c2VkIGFzIGEgcmVmZXJlbmNlDQojID09PT09PT09PT09PT09PT09PT09DQoNCiMgR2VuZXJhdGluZyBsaW5lYXIgaW1wdXRhdGlvbiBtb2RlbCBmb3IgRW1wX2R1cmF0aW9uDQpOdW1JbXB1dGVfTW9kZWxfRW1wX2R1cmF0aW9uIDwtIGxtKA0KICBFbXBfZHVyYXRpb24gfiANCiAgICAjIERlZmF1bHQgKyANCiAgICAjIENoZWNraW5nX2Ftb3VudCArIA0KICAgICMgVGVybSArIA0KICAgICMgQ3JlZGl0X3Njb3JlICsgDQogICAgIyAjIEdlbmRlciArIA0KICAgICMgIyBNYXJpdGFsX3N0YXR1cyArIA0KICAgICMgQ2FyX2xvYW4gKyANCiAgICAjIFBlcnNvbmFsX2xvYW4gKyANCiAgICAjIEhvbWVfbG9hbiArIA0KICAgICMgRWR1Y2F0aW9uX2xvYW4gKyANCiAgICAjICMgRW1wX3N0YXR1cyArIA0KICAgICMgQW1vdW50ICsgDQogICAgIyBTYXZpbmdfYW1vdW50ICsgDQogICAgIyBFbXBfZHVyYXRpb24gKw0KICAgICMgQWdlICsgDQogICAgTm9fb2ZfY3JlZGl0X2FjYywNCiAgZGF0YSA9IGxvYW5fZGVmYXVsdF9Nb2QwNA0KKQ0KDQojIEdlbmVyYXRpbmcgbGluZWFyIGltcHV0YXRpb24gbW9kZWwgZm9yIEFnZQ0KTnVtSW1wdXRlX01vZGVsX0FnZSA8LSBsbSgNCiAgQWdlIH4gDQogICAgIyBEZWZhdWx0ICsgDQogICAgIyBDaGVja2luZ19hbW91bnQgKyANCiAgICAjIFRlcm0gKyANCiAgICAjIENyZWRpdF9zY29yZSArDQogICAgIyBHZW5kZXIgKyANCiAgICAjIE1hcml0YWxfc3RhdHVzICsgDQogICAgIyBDYXJfbG9hbiArIA0KICAgICMgUGVyc29uYWxfbG9hbiArIA0KICAgICMgSG9tZV9sb2FuICsgDQogICAgIyBFZHVjYXRpb25fbG9hbiArIA0KICAgICMgRW1wX3N0YXR1cyArIA0KICAgICMgQW1vdW50ICsgDQogICAgU2F2aW5nX2Ftb3VudCwNCiAgICAjIEVtcF9kdXJhdGlvbiArDQogICAgIyBBZ2UgKyANCiAgICAjIE5vX29mX2NyZWRpdF9hY2MsDQogIGRhdGEgPSBsb2FuX2RlZmF1bHRfTW9kMDQNCikNCg0KIyBDb3B5aW5nIGxvYW5fZGVmYXVsdF9Nb2QwNA0KTnVtSW1wdXRlIDwtIGxvYW5fZGVmYXVsdF9Nb2QwNA0KDQojIEltcHV0aW5nIEVtcF9kdXJhdGlvbiB3aXRoIHJhbmRvbSByZWdyZXNzaW9uDQpOdW1JbXB1dGUkRW1wX2R1cmF0aW9uW2lzLm5hKGxvYW5fZGVmYXVsdF9Nb2QwNCRFbXBfZHVyYXRpb24pXSA8LSANCiAgIyBQcmVkaWN0ZWQgdmFsdWUNCiAgcHJlZGljdCgNCiAgICBOdW1JbXB1dGVfTW9kZWxfRW1wX2R1cmF0aW9uLCANCiAgICBsb2FuX2RlZmF1bHRfTW9kMDRbaXMubmEobG9hbl9kZWZhdWx0X01vZDA0JEVtcF9kdXJhdGlvbiksXSwgDQogICAgdHlwZSA9ICJyZXNwb25zZSINCiAgKSArDQogICMgQWRkZWQgcmFuZG9tbmVzcyB2aWEgcmVzaWR1YWwNCiAgc2FtcGxlKA0KICAgIHJlc2lkKE51bUltcHV0ZV9Nb2RlbF9FbXBfZHVyYXRpb24pLCANCiAgICBzdW0oaXMubmEobG9hbl9kZWZhdWx0X01vZDA0JEVtcF9kdXJhdGlvbikpLCANCiAgICByZXBsYWNlID0gVFJVRQ0KICApDQoNCiMgSW1wdXRpbmcgQWdlIHdpdGggcmFuZG9tIHJlZ3Jlc3Npb24NCk51bUltcHV0ZSRBZ2VbaXMubmEobG9hbl9kZWZhdWx0X01vZDA0JEFnZSldIDwtIA0KICAjIFByZWRpY3RlZCB2YWx1ZQ0KICBwcmVkaWN0KA0KICAgIE51bUltcHV0ZV9Nb2RlbF9BZ2UsIA0KICAgIGxvYW5fZGVmYXVsdF9Nb2QwNFtpcy5uYShsb2FuX2RlZmF1bHRfTW9kMDQkQWdlKSxdLCANCiAgICB0eXBlID0gInJlc3BvbnNlIg0KICApICsNCiAgIyBBZGRlZCByYW5kb21uZXNzIHZpYSByZXNpZHVhbA0KICBzYW1wbGUoDQogICAgcmVzaWQoTnVtSW1wdXRlX01vZGVsX0FnZSksIA0KICAgIHN1bShpcy5uYShsb2FuX2RlZmF1bHRfTW9kMDQkQWdlKSksIA0KICAgIHJlcGxhY2UgPSBUUlVFDQogICkNCmBgYA0KDQpgYGB7cn0NCiMgPT09PT09PT09PT09PT09PT09PT0NCiMgUExPVFRJTkcgRW1wX2R1cmF0aW9uIEFORCBBZ2UgV0lUSCBJTVBVVEVEIFZBTFVFUw0KIyA9PT09PT09PT09PT09PT09PT09PQ0KDQpQbG90X051bUltcF9QYXJ0cyA8LSBOVUxMDQoNCiMgR2VuZXJhdGluZyBzdWJwbG90DQpQbG90X051bUltcF9QYXJ0c1tbMV1dIDwtIA0KICBwbG90X2x5KCkgJT4lIA0KICBhZGRfdHJhY2UoDQogICAgZGF0YSA9IGxvYW5fZGVmYXVsdF9Nb2QwNCwNCiAgICB4ID0gfkVtcF9kdXJhdGlvbiwNCiAgICBuYW1lID0gIk5vbi1taXNzaW5nIiwNCiAgICBsZWdlbmRncm91cCA9ICJFbXBfZHVyYXRpb24iLA0KICAgIGxlZ2VuZGdyb3VwdGl0bGUgPSBsaXN0KHRleHQgPSAiRW1wX2R1cmF0aW9uIikNCiAgKSAlPiUgDQogIGFkZF90cmFjZSgNCiAgICB4ID0gTnVtSW1wdXRlJEVtcF9kdXJhdGlvbltpcy5uYShsb2FuX2RlZmF1bHRfTW9kMDQkRW1wX2R1cmF0aW9uKV0sDQogICAgbmFtZSA9ICJJbXB1dGVkOiBFbXBfRHVyYXRpb24iLA0KICAgIGxlZ2VuZGdyb3VwID0gIkVtcF9kdXJhdGlvbiIsDQogICAgbGVnZW5kZ3JvdXB0aXRsZSA9IGxpc3QodGV4dCA9ICJFbXBfZHVyYXRpb24iKQ0KICApDQoNCiMgR2VuZXJhdGluZyBzdWJwbG90DQpQbG90X051bUltcF9QYXJ0c1tbMl1dIDwtIA0KICBwbG90X2x5KCkgJT4lIA0KICBhZGRfdHJhY2UoDQogICAgZGF0YSA9IGxvYW5fZGVmYXVsdF9Nb2QwNCwNCiAgICB4ID0gfkFnZSwNCiAgICBuYW1lID0gIk5vbi1taXNzaW5nIiwNCiAgICBsZWdlbmRncm91cCA9ICJBZ2UiLA0KICAgIGxlZ2VuZGdyb3VwdGl0bGUgPSBsaXN0KHRleHQgPSAiQWdlIikNCiAgKSAlPiUgDQogIGFkZF90cmFjZSgNCiAgICB4ID0gTnVtSW1wdXRlJEFnZVtpcy5uYShsb2FuX2RlZmF1bHRfTW9kMDQkQWdlKV0sDQogICAgbmFtZSA9ICJJbXB1dGVkOiBBZ2UiLA0KICAgIGxlZ2VuZGdyb3VwID0gIkFnZSIsDQogICAgbGVnZW5kZ3JvdXB0aXRsZSA9IGxpc3QodGV4dCA9ICJBZ2UiKQ0KICApDQoNCiMgR2VuZXJhdGluZyBmdWxsIHBsb3QNClBsb3RfTnVtSW1wIDwtIA0KICBzdWJwbG90KFBsb3RfTnVtSW1wX1BhcnRzW1sxXV0sIFBsb3RfTnVtSW1wX1BhcnRzW1syXV0sIG5yb3dzID0gMSwgbWFyZ2luID0gMC4wNSwgc2hhcmVZID0gVFJVRSkgJT4lIA0KICBsYXlvdXQoDQogICAgdGl0bGUgPSAiRW1wbG95bWVudCBEdXJhdGlvbiBhbmQgQWdlIHdpdGggSW1wdXRhdGlvbiIsDQogICAgYmFybW9kZSA9ICJzdGFjayIsDQogICAgYmFyZ2FwID0gMC4xLA0KICAgIGxlZ2VuZCA9IGxpc3QoDQogICAgICB0aXRsZSA9IGxpc3QodGV4dCA9ICI8Yj4gUmVzcG9uc2VzIDwvYj4iKSwNCiAgICAgIGJnY29sb3IgPSAiI0UyRTJFMiIsDQogICAgICBib3JkZXJjb2xvciA9ICIjRkZGRkZGIiwNCiAgICAgIGJvcmRlcndpZHRoID0gMg0KICAgICkNCiAgKQ0KDQojIE91dHB1dHRpbmcgcGxvdA0KUGxvdF9OdW1JbXANCmBgYA0KDQpJdCBpcyB3b3J0aCBub3RpbmcgdGhhdCB0aGlzIG1ldGhvZCBoYXMgdGhlIHBvc3NpYmlsaXR5IG9mIGltcHV0aW5nIHZhbHVlcyBvdXRzaWRlIG9mIHRoZSBwb3NzaWJsZSByYW5nZSBvZiB0aGUgZGF0YS4gVGhlcmVmb3JlLCBpdCBpcyB3b3J0aCBjb25zaWRlcmluZyBhbiBhbHRlcm5hdGl2ZSBtZXRob2Qgb2YgaW1wdXRhdGlvbi4NCg0KIyBNdWx0aXBsZSBJbXB1dGF0aW9uIGJ5IENoYWluZWQgRXF1YXRpb25zIChNSUNFKQ0KDQpNdWx0aXBsZSBJbXB1dGF0aW9uIGJ5IENoYWluZWQgRXF1YXRpb25zIChNSUNFKSBpcyBhIG1ldGhvZCBvZiBpbXB1dGluZyB0aGF0IGl0ZXJhdGVzIHRocm91Z2ggZWFjaCB2YXJpYWJsZSBjb250YWluaW5nIG1pc3NpbmcgZGF0YSBhbmQgcmVmaW5pbmcgZXN0aW1hdGVzLiBUaGVyZWZvcmUsIHRoaXMgbWV0aG9kIGFsbG93cyB1cyB0byBpbXB1dGUgYEdlbmRlcmAsIGBNYXJpdGFsX3N0YXR1c2AsIGBFbXBfc3RhdHVzYCwgYEVtcF9kdXJhdGlvbmAsIGFuZCBgQWdlYCBhdCB0aGUgc2FtZSB0aW1lLg0KDQpUaGUgcmVzdWx0cyBvZiBNSUNFIGNhbiBiZSBzZWVuIGluIHRoZSBwbG90cyBiZWxvdzoNCg0KYGBge3J9DQojID09PT09PT09PT09PT09PT09PT09DQojIFBFUkZPUk1JTkcgTVVMVElQTEUgSU1QVVRBVElPTg0KIyA9PT09PT09PT09PT09PT09PT09PQ0KDQpsb2FuX2RlZmF1bHRfTXVsdEltcCA8LSANCiAgY29tcGxldGUoDQogICAgbWljZSgNCiAgICAgIGxvYW5fZGVmYXVsdF9Nb2QwNFtSYXdWYXJzXSwNCiAgICAgIG1ldGhvZCA9IGMoDQogICAgICAgICMgRGVmYXVsdCANCiAgICAgICAgTkEsDQogICAgICAgICMgQ2hlY2tpbmdfYW1vdW50IA0KICAgICAgICBOQSwNCiAgICAgICAgIyBUZXJtIA0KICAgICAgICBOQSwNCiAgICAgICAgIyBDcmVkaXRfc2NvcmUgDQogICAgICAgIE5BLA0KICAgICAgICAjIEdlbmRlciANCiAgICAgICAgImxvZ3JlZyIsDQogICAgICAgICMgTWFyaXRhbF9zdGF0dXMgDQogICAgICAgICJsb2dyZWciLA0KICAgICAgICAjIENhcl9sb2FuIA0KICAgICAgICBOQSwNCiAgICAgICAgIyBQZXJzb25hbF9sb2FuIA0KICAgICAgICBOQSwNCiAgICAgICAgIyBIb21lX2xvYW4gDQogICAgICAgIE5BLA0KICAgICAgICAjIEVkdWNhdGlvbl9sb2FuIA0KICAgICAgICBOQSwNCiAgICAgICAgIyBFbXBfc3RhdHVzIA0KICAgICAgICAibG9ncmVnIiwNCiAgICAgICAgIyBBbW91bnQgDQogICAgICAgIE5BLA0KICAgICAgICAjIFNhdmluZ19hbW91bnQgDQogICAgICAgIE5BLA0KICAgICAgICAjIEVtcF9kdXJhdGlvbg0KICAgICAgICAicG1tIiwNCiAgICAgICAgIyBBZ2UgDQogICAgICAgICJwbW0iLA0KICAgICAgICAjIE5vX29mX2NyZWRpdF9hY2MNCiAgICAgICAgTkENCiAgICAgICksDQogICAgICBtYXhpdCA9IDUsDQogICAgICBwcmludCA9IEYsDQogICAgICBzZWVkID0gMTIzDQogICAgKQ0KICApDQpgYGANCg0KYGBge3J9DQojID09PT09PT09PT09PT09PT09PT09DQojIFBMT1RUSU5HIENBVEVHT1JJQ0FMIFZBUklBQkxFUyBXSVRIIE1JQ0UNCiMgPT09PT09PT09PT09PT09PT09PT0NCg0KDQpNSUNFX0NhdEltcF9HZW5kZXJfQ29tYm8gPC0gZGF0YS5mcmFtZSgNCiAgQ2F0ID0gYygiRmVtYWxlIiwgIkZlbWFsZSAoSW1wdXRlZCkiLCAiTWFsZSIsICJNYWxlIChJbXB1dGVkKSIsICJOQSAoSW1wdXRlZCkiKSwNCiAgVmFsID0gYygNCiAgICB0YWJsZShsb2FuX2RlZmF1bHRfTW9kMDQkR2VuZGVyKVsiRmVtYWxlIl0sDQogICAgdGFibGUobG9hbl9kZWZhdWx0X011bHRJbXAkR2VuZGVyW2lzLm5hKGxvYW5fZGVmYXVsdF9Nb2QwNCRHZW5kZXIpXSwgdXNlTkEgPSAiYWx3YXlzIilbIkZlbWFsZSJdLA0KICAgIHRhYmxlKGxvYW5fZGVmYXVsdF9Nb2QwNCRHZW5kZXIpWyJNYWxlIl0sDQogICAgdGFibGUobG9hbl9kZWZhdWx0X011bHRJbXAkR2VuZGVyW2lzLm5hKGxvYW5fZGVmYXVsdF9Nb2QwNCRHZW5kZXIpXSwgdXNlTkEgPSAiYWx3YXlzIilbIk1hbGUiXSwNCiAgICB0YWJsZShsb2FuX2RlZmF1bHRfTXVsdEltcCRHZW5kZXJbaXMubmEobG9hbl9kZWZhdWx0X01vZDA0JEdlbmRlcildLCB1c2VOQSA9ICJhbHdheXMiKVszXQ0KICApDQopDQoNCk1JQ0VfQ2F0SW1wX0dlbmRlcl9Db21ibyRDYXQgPC0gTUlDRV9DYXRJbXBfR2VuZGVyX0NvbWJvJENhdCAlPiUgDQogIGZhY3RvcihsZXZlbHMgPSBNSUNFX0NhdEltcF9HZW5kZXJfQ29tYm9bWyJDYXQiXV0pDQoNCg0KTUlDRV9DYXRJbXBfTWFyaXRhbF9zdGF0dXNfQ29tYm8gPC0gZGF0YS5mcmFtZSgNCiAgQ2F0ID0gYygiTWFycmllZCIsICJNYXJyaWVkIChJbXB1dGVkKSIsICJTaW5nbGUiLCAiU2luZ2xlIChJbXB1dGVkKSIsICJOQSAoSW1wdXRlZCkiKSwNCiAgVmFsID0gYygNCiAgICB0YWJsZShsb2FuX2RlZmF1bHRfTW9kMDQkTWFyaXRhbF9zdGF0dXMpWyJNYXJyaWVkIl0sDQogICAgdGFibGUobG9hbl9kZWZhdWx0X011bHRJbXAkTWFyaXRhbF9zdGF0dXNbaXMubmEobG9hbl9kZWZhdWx0X01vZDA0JE1hcml0YWxfc3RhdHVzKV0sIHVzZU5BID0gImFsd2F5cyIpWyJNYXJyaWVkIl0sDQogICAgdGFibGUobG9hbl9kZWZhdWx0X01vZDA0JE1hcml0YWxfc3RhdHVzKVsiU2luZ2xlIl0sDQogICAgdGFibGUobG9hbl9kZWZhdWx0X011bHRJbXAkTWFyaXRhbF9zdGF0dXNbaXMubmEobG9hbl9kZWZhdWx0X01vZDA0JE1hcml0YWxfc3RhdHVzKV0sIHVzZU5BID0gImFsd2F5cyIpWyJTaW5nbGUiXSwNCiAgICB0YWJsZShsb2FuX2RlZmF1bHRfTXVsdEltcCRNYXJpdGFsX3N0YXR1c1tpcy5uYShsb2FuX2RlZmF1bHRfTW9kMDQkTWFyaXRhbF9zdGF0dXMpXSwgdXNlTkEgPSAiYWx3YXlzIilbM10NCiAgKQ0KKQ0KDQpNSUNFX0NhdEltcF9NYXJpdGFsX3N0YXR1c19Db21ibyRDYXQgPC0gTUlDRV9DYXRJbXBfTWFyaXRhbF9zdGF0dXNfQ29tYm8kQ2F0ICU+JSANCiAgZmFjdG9yKGxldmVscyA9IE1JQ0VfQ2F0SW1wX01hcml0YWxfc3RhdHVzX0NvbWJvW1siQ2F0Il1dKQ0KDQoNCk1JQ0VfQ2F0SW1wX0VtcF9zdGF0dXNfQ29tYm8gPC0gZGF0YS5mcmFtZSgNCiAgQ2F0ID0gYygiRW1wbG95ZWQiLCAiRW1wbG95ZWQgKEltcHV0ZWQpIiwgIlVuZW1wbG95ZWQiLCAiVW5lbXBsb3llZCAoSW1wdXRlZCkiLCAiTkEgKEltcHV0ZWQpIiksDQogIFZhbCA9IGMoDQogICAgdGFibGUobG9hbl9kZWZhdWx0X01vZDA0JEVtcF9zdGF0dXMpWyJFbXBsb3llZCJdLA0KICAgIHRhYmxlKGxvYW5fZGVmYXVsdF9NdWx0SW1wJEVtcF9zdGF0dXNbaXMubmEobG9hbl9kZWZhdWx0X01vZDA0JEVtcF9zdGF0dXMpXSwgdXNlTkEgPSAiYWx3YXlzIilbIkVtcGxveWVkIl0sDQogICAgdGFibGUobG9hbl9kZWZhdWx0X01vZDA0JEVtcF9zdGF0dXMpWyJVbmVtcGxveWVkIl0sDQogICAgdGFibGUobG9hbl9kZWZhdWx0X011bHRJbXAkRW1wX3N0YXR1c1tpcy5uYShsb2FuX2RlZmF1bHRfTW9kMDQkRW1wX3N0YXR1cyldLCB1c2VOQSA9ICJhbHdheXMiKVsiVW5lbXBsb3llZCJdLA0KICAgIHRhYmxlKGxvYW5fZGVmYXVsdF9NdWx0SW1wJEVtcF9zdGF0dXNbaXMubmEobG9hbl9kZWZhdWx0X01vZDA0JEVtcF9zdGF0dXMpXSwgdXNlTkEgPSAiYWx3YXlzIilbM10NCiAgKQ0KKQ0KDQpNSUNFX0NhdEltcF9FbXBfc3RhdHVzX0NvbWJvJENhdCA8LSBNSUNFX0NhdEltcF9FbXBfc3RhdHVzX0NvbWJvJENhdCAlPiUgDQogIGZhY3RvcihsZXZlbHMgPSBNSUNFX0NhdEltcF9FbXBfc3RhdHVzX0NvbWJvW1siQ2F0Il1dKQ0KDQoNCiMgPT09PT09PT09PT09PT09PT09PT09PT09DQoNClBsb3RfTXVsdEltcF9DYXRfUGFydHMgPC0gTlVMTA0KDQojIEdlbmVyYXRpbmcgc3VicGxvdA0KUGxvdF9NdWx0SW1wX0NhdF9QYXJ0c1tbMV1dIDwtIA0KICBwbG90X2x5KCkgJT4lIA0KICBhZGRfdHJhY2UoDQogICAgZGF0YSA9IE1JQ0VfQ2F0SW1wX0dlbmRlcl9Db21ibywNCiAgICB4ID0gIkdlbmRlciIsDQogICAgeSA9IH5WYWwsDQogICAgbmFtZSA9IH5DYXQsDQogICAgdHlwZSA9ICJiYXIiLA0KICAgIGxlZ2VuZGdyb3VwID0gIkdlbmRlciIsDQogICAgbGVnZW5kZ3JvdXB0aXRsZSA9IGxpc3QodGV4dCA9ICJHZW5kZXIiKQ0KICApICU+JSANCiAgbGF5b3V0KGJhcm1vZGUgPSAiZ3JvdXAiKQ0KDQojIEdlbmVyYXRpbmcgc3VicGxvdA0KUGxvdF9NdWx0SW1wX0NhdF9QYXJ0c1tbMl1dIDwtIA0KICBwbG90X2x5KCkgJT4lIA0KICBhZGRfdHJhY2UoDQogICAgZGF0YSA9IE1JQ0VfQ2F0SW1wX01hcml0YWxfc3RhdHVzX0NvbWJvLA0KICAgIHggPSAiTWFyaXRhbF9zdGF0dXMiLA0KICAgIHkgPSB+VmFsLA0KICAgIG5hbWUgPSB+Q2F0LA0KICAgIHR5cGUgPSAiYmFyIiwNCiAgICBsZWdlbmRncm91cCA9ICJNYXJpdGFsX3N0YXR1cyIsDQogICAgbGVnZW5kZ3JvdXB0aXRsZSA9IGxpc3QodGV4dCA9ICJNYXJpdGFsX3N0YXR1cyIpDQogICkgJT4lIA0KICBsYXlvdXQoYmFybW9kZSA9ICJncm91cCIpDQoNCiMgR2VuZXJhdGluZyBzdWJwbG90DQpQbG90X011bHRJbXBfQ2F0X1BhcnRzW1szXV0gPC0gDQogIHBsb3RfbHkoKSAlPiUgDQogIGFkZF90cmFjZSgNCiAgICBkYXRhID0gTUlDRV9DYXRJbXBfRW1wX3N0YXR1c19Db21ibywNCiAgICB4ID0gIkVtcF9zdGF0dXMiLA0KICAgIHkgPSB+VmFsLA0KICAgIG5hbWUgPSB+Q2F0LA0KICAgIHR5cGUgPSAiYmFyIiwNCiAgICBsZWdlbmRncm91cCA9ICJFbXBfc3RhdHVzIiwNCiAgICBsZWdlbmRncm91cHRpdGxlID0gbGlzdCh0ZXh0ID0gIkVtcF9zdGF0dXMiKQ0KICApICU+JSANCiAgbGF5b3V0KGJhcm1vZGUgPSAiZ3JvdXAiKQ0KDQoNCiMgR2VuZXJhdGluZyBmdWxsIHBsb3QNClBsb3RfTXVsdEltcF9DYXQgPC0gDQogIHN1YnBsb3QoUGxvdF9NdWx0SW1wX0NhdF9QYXJ0c1tbMV1dLCBQbG90X011bHRJbXBfQ2F0X1BhcnRzW1syXV0sIFBsb3RfTXVsdEltcF9DYXRfUGFydHNbWzNdXSwgbnJvd3MgPSAxLCBtYXJnaW4gPSAwLjA1LCBzaGFyZVkgPSBUUlVFKSAlPiUgDQogIGxheW91dCgNCiAgICB0aXRsZSA9ICJDYXRlZ29yaWNhbCBNSUNFIFJlc3VsdHMiLA0KICAgIGJhcmdhcCA9IDAuMSwNCiAgICB5YXhpcyA9IGxpc3QoDQogICAgICB0aXRsZSA9ICIiDQogICAgKSwNCiAgICBsZWdlbmQgPSBsaXN0KA0KICAgICAgdGl0bGUgPSBsaXN0KHRleHQgPSAiPGI+IFJlc3BvbnNlcyA8L2I+IiksDQogICAgICBiZ2NvbG9yID0gIiNFMkUyRTIiLA0KICAgICAgYm9yZGVyY29sb3IgPSAiI0ZGRkZGRiIsDQogICAgICBib3JkZXJ3aWR0aCA9IDINCiAgICApDQogICkNCg0KIyBPdXRwdXR0aW5nIHBsb3QNClBsb3RfTXVsdEltcF9DYXQNCmBgYA0KDQpGb3IgdGhlIGNhdGVnb3JpY2FsIHZhcmlhYmxlcywgTUlDRSBhcHBlYXJzIHRvIGhhdmUgcmVzdWx0ZWQgaW4gc2ltaWxhciBpbXB1dGF0aW9uIGFzIHdpdGggKmsqLU5OIGJ1dCBoYXMgcmV0dXJuZWQgbWlzc2luZyB2YWx1ZXMuIFRoZXJlZm9yZSwgTUlDRSBpbXB1dGF0aW9uIG1heSBub3QgYmUgdGhlIG9wdGltYWwgd2F5IHRvIGltcHV0ZSBjYXRlZ29yaWNhbCB2YXJpYWJsZXMuDQoNCmBgYHtyfQ0KIyA9PT09PT09PT09PT09PT09PT09PQ0KIyBQTE9UVElORyBOVU1FUklDQUwgVkFSSUFCTEVTIFdJVEggTUlDRQ0KIyA9PT09PT09PT09PT09PT09PT09PQ0KDQoNClBsb3RfTXVsdEltcF9OdW1fUGFydHMgPC0gTlVMTA0KDQojIEdlbmVyYXRpbmcgc3VicGxvdA0KUGxvdF9NdWx0SW1wX051bV9QYXJ0c1tbMV1dIDwtIA0KICBwbG90X2x5KCkgJT4lIA0KICBhZGRfdHJhY2UoDQogICAgZGF0YSA9IGxvYW5fZGVmYXVsdF9Nb2QwNCwNCiAgICB4ID0gfkVtcF9kdXJhdGlvbiwNCiAgICBuYW1lID0gIk5vbi1taXNzaW5nIiwNCiAgICBsZWdlbmRncm91cCA9ICJFbXBfZHVyYXRpb24iLA0KICAgIGxlZ2VuZGdyb3VwdGl0bGUgPSBsaXN0KHRleHQgPSAiRW1wX2R1cmF0aW9uIikNCiAgKSAlPiUgDQogIGFkZF90cmFjZSgNCiAgICB4ID0gbG9hbl9kZWZhdWx0X011bHRJbXAkRW1wX2R1cmF0aW9uW2lzLm5hKGxvYW5fZGVmYXVsdF9Nb2QwNCRFbXBfZHVyYXRpb24pXSwNCiAgICBuYW1lID0gIkltcHV0ZWQ6IEVtcF9EdXJhdGlvbiIsDQogICAgbGVnZW5kZ3JvdXAgPSAiRW1wX2R1cmF0aW9uIiwNCiAgICBsZWdlbmRncm91cHRpdGxlID0gbGlzdCh0ZXh0ID0gIkVtcF9kdXJhdGlvbiIpDQogICkgJT4lIA0KICBsYXlvdXQoYmFybW9kZSA9ICJzdGFjayIpDQoNCiMgR2VuZXJhdGluZyBzdWJwbG90DQpQbG90X011bHRJbXBfTnVtX1BhcnRzW1syXV0gPC0gDQogIHBsb3RfbHkoKSAlPiUgDQogIGFkZF90cmFjZSgNCiAgICBkYXRhID0gbG9hbl9kZWZhdWx0X01vZDA0LA0KICAgIHggPSB+QWdlLA0KICAgIG5hbWUgPSAiTm9uLW1pc3NpbmciLA0KICAgIGxlZ2VuZGdyb3VwID0gIkFnZSIsDQogICAgbGVnZW5kZ3JvdXB0aXRsZSA9IGxpc3QodGV4dCA9ICJBZ2UiKQ0KICApICU+JSANCiAgYWRkX3RyYWNlKA0KICAgIHggPSBsb2FuX2RlZmF1bHRfTXVsdEltcCRBZ2VbaXMubmEobG9hbl9kZWZhdWx0X01vZDA0JEFnZSldLA0KICAgIG5hbWUgPSAiSW1wdXRlZDogQWdlIiwNCiAgICBsZWdlbmRncm91cCA9ICJBZ2UiLA0KICAgIGxlZ2VuZGdyb3VwdGl0bGUgPSBsaXN0KHRleHQgPSAiQWdlIikNCiAgKSAlPiUgDQogIGxheW91dChiYXJtb2RlID0gInN0YWNrIikNCg0KIyBHZW5lcmF0aW5nIGZ1bGwgcGxvdA0KUGxvdF9NdWx0SW1wX051bSA8LSANCiAgc3VicGxvdChQbG90X011bHRJbXBfTnVtX1BhcnRzW1sxXV0sIFBsb3RfTXVsdEltcF9OdW1fUGFydHNbWzJdXSwgbnJvd3MgPSAxLCBtYXJnaW4gPSAwLjA1LCBzaGFyZVkgPSBUUlVFKSAlPiUgDQogIGxheW91dCgNCiAgICB0aXRsZSA9ICJOdW1lcmljYWwgTUlDRSBSZXN1bHRzIiwNCiAgICBiYXJnYXAgPSAwLjEsDQogICAgbGVnZW5kID0gbGlzdCgNCiAgICAgIHRpdGxlID0gbGlzdCh0ZXh0ID0gIjxiPiBSZXNwb25zZXMgPC9iPiIpLA0KICAgICAgYmdjb2xvciA9ICIjRTJFMkUyIiwNCiAgICAgIGJvcmRlcmNvbG9yID0gIiNGRkZGRkYiLA0KICAgICAgYm9yZGVyd2lkdGggPSAyDQogICAgKQ0KICApDQoNCiMgT3V0cHV0dGluZyBwbG90DQpQbG90X011bHRJbXBfTnVtDQpgYGANCg0KRm9yIHRoZSBudW1lcmljYWwgdmFyaWFibGVzLCBNSUNFIGFwcGVhcnMgdG8gaGF2ZSByZXN1bHRlZCBpbiBzaW1pbGFyIGltcHV0YXRpb24gYXMgd2l0aCByYW5kb20gcmVncmVzc2lvbiBidXQgZG9lcyBub3QgZ2VuZXJhdGUgdmFsdWVzIG91dHNpZGUgb2YgdGhlIHBvc3NpYmxlIHJhbmdlLiBUaGlzIGltcHJvdmVtZW50IG1ha2VzIE1JQ0UgcHJlZmVyYWJsZSB0byByYW5kb20gcmVncmVzc2lvbiBpbXB1dGF0aW9uLg0KDQoNCg==